HomeLab.psm1
<#
.SYNOPSIS HomeLab Aggregator Module .DESCRIPTION Aggregates functionality from HomeLab.Core, HomeLab.Azure, HomeLab.Security, HomeLab.UI, and HomeLab.Monitoring. Provides the high-level Start-HomeLab function as the main entry point for the application. .NOTES Author: Jurie Smit Date: March 10, 2025 Version: 1.0.0 #> # ===== CRITICAL SECTION: PREVENT INFINITE LOOPS ===== # Disable automatic module loading to prevent recursive loading $PSModuleAutoLoadingPreference = 'None' # Disable function discovery debugging which can cause infinite loops $DebugPreference = 'SilentlyContinue' #region Script Variables $script:Version = "1.0.0" $script:StartTime = Get-Date $script:ModulesLoaded = $false $script:ConfigLoaded = $false # Updated to reflect local module paths $script:RequiredModules = @( @{Name = "HomeLab.Logging"; Path = "$PSScriptRoot\modules\HomeLab.Logging\HomeLab.Logging.psm1"}, @{Name = "HomeLab.Utils"; Path = "$PSScriptRoot\modules\HomeLab.Utils\HomeLab.Utils.psm1"}, @{Name = "HomeLab.Core"; Path = "$PSScriptRoot\modules\HomeLab.Core\HomeLab.Core.psm1"}, @{Name = "HomeLab.UI"; Path = "$PSScriptRoot\modules\HomeLab.UI\HomeLab.UI.psm1"}, @{Name = "HomeLab.Azure"; Path = "$PSScriptRoot\modules\HomeLab.Azure\HomeLab.Azure.psm1"}, @{Name = "HomeLab.Security"; Path = "$PSScriptRoot\modules\HomeLab.Security\HomeLab.Security.psm1"}, @{Name = "HomeLab.Monitoring"; Path = "$PSScriptRoot\modules\HomeLab.Monitoring\HomeLab.Monitoring.psm1"} # @{Name = "Az"; MinVersion = "9.0.0"} # Az is the only external module ) $script:State = @{ ConfigPath = "$env:USERPROFILE\.homelab\config.json" # Set a default value LogLevel = "Info" # Set a default value Config = $null User = $env:USERNAME AzContext = $null ConnectionStatus = "Disconnected" LastDeployment = $null MenuHistory = @() } $script:LogFile = "$env:USERPROFILE\.homelab\logs\homelab_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" #endregion # Save the original PSDefaultParameterValues at the start to prevent infinite loops $originalPSDefaultParameterValues = $null if ($global:PSDefaultParameterValues) { $originalPSDefaultParameterValues = $global:PSDefaultParameterValues.Clone() # Clear it to prevent infinite loops during module loading $global:PSDefaultParameterValues = @{} } # Use $PSScriptRoot if available; otherwise, fall back to the current directory. if ([string]::IsNullOrWhiteSpace($PSScriptRoot)) { [string]$scriptDirectory = (Get-Location).Path } else { [string]$scriptDirectory = $PSScriptRoot } Write-Verbose "Using script directory: $scriptDirectory" # Force $modulesRoot to be a single string. [string]$modulesRoot = Join-Path -Path $scriptDirectory -ChildPath "modules" Write-Verbose "Modules root set to: $modulesRoot" # Define the Functions directory path [string]$functionsDirectory = Join-Path -Path $scriptDirectory -ChildPath "Functions" Write-Verbose "Functions directory set to: $functionsDirectory" # List paths for each submodule's main PSM1. [string]$loggingModulePath = Join-Path -Path $modulesRoot -ChildPath "HomeLab.Logging\HomeLab.Logging.psm1" [string]$coreModulePath = Join-Path -Path $modulesRoot -ChildPath "HomeLab.Core\HomeLab.Core.psm1" [string]$utilsModulePath = Join-Path -Path $modulesRoot -ChildPath "HomeLab.Utils\HomeLab.Utils.psm1" [string]$azureModulePath = Join-Path -Path $modulesRoot -ChildPath "HomeLab.Azure\HomeLab.Azure.psm1" [string]$securityModulePath = Join-Path -Path $modulesRoot -ChildPath "HomeLab.Security\HomeLab.Security.psm1" [string]$monitoringModulePath = Join-Path -Path $modulesRoot -ChildPath "HomeLab.Monitoring\HomeLab.Monitoring.psm1" [string]$uiModulePath = Join-Path -Path $modulesRoot -ChildPath "HomeLab.UI\HomeLab.UI.psm1" # Load core modules in the correct order $moduleOrder = @( $loggingModulePath, # Fixed: removed comma and added $ $utilsModulePath, # Fixed: removed comma and added $ $coreModulePath, # Fixed: removed comma and added $ $uiModulePath, # Fixed: removed comma and added $ $azureModulePath, # Fixed: removed comma and added $ $securityModulePath, # Fixed: removed comma and added $ $monitoringModulePath # Fixed: removed comma and added $ ) # Track which modules were successfully loaded $loadedModules = @{} # Import each module in order foreach ($modulePath in $moduleOrder) { if (Test-Path -Path $modulePath) { Write-Verbose "Loading module from path: $modulePath" try { Import-Module -Name $modulePath -Global -Force -DisableNameChecking -ErrorAction Stop # Get the module name from the path $moduleName = [System.IO.Path]::GetFileNameWithoutExtension($modulePath) $moduleName = $moduleName.Split('\')[-1] # Check if the module was loaded correctly $loadedModules[$modulePath] = (Get-Module -Name $moduleName -ErrorAction SilentlyContinue) -ne $null if ($loadedModules[$modulePath]) { Write-Verbose "Successfully loaded module: $moduleName" } else { Write-Warning "Module loaded but not found in Get-Module: $moduleName" } } catch { Write-Warning "Failed to load module from path: $modulePath. Error: $_" $loadedModules[$modulePath] = $false } } else { Write-Warning "Module path not found: $modulePath" $loadedModules[$modulePath] = $false } } # Create the Functions directory if it doesn't exist if (-not (Test-Path -Path $functionsDirectory)) { try { New-Item -Path $functionsDirectory -ItemType Directory -Force | Out-Null Write-Verbose "Created Functions directory: $functionsDirectory" } catch { Write-Error "Failed to create Functions directory: $_" } } # Import all function files from the Functions directory if (Test-Path $functionsDirectory) { Write-Verbose "Importing functions from directory: $functionsDirectory" $functionFiles = Get-ChildItem -Path $functionsDirectory -Filter "*.ps1" -ErrorAction SilentlyContinue if ($functionFiles.Count -gt 0) { foreach ($functionFile in $functionFiles) { try { Write-Verbose "Importing function file: $($functionFile.FullName)" . $functionFile.FullName Write-Verbose "Successfully imported function file: $($functionFile.Name)" } catch { Write-Error "Failed to import function file $($functionFile.Name): $_" } } } else { Write-Warning "No function files found in directory: $functionsDirectory" } } # Explicitly re-export the Show-Menu function from HomeLab.UI to ensure it's available if (Get-Module -Name "HomeLab.UI" -ErrorAction SilentlyContinue) { try { # Check if the function exists in the module $uiModule = Get-Module -Name "HomeLab.UI" $hasShowMenu = $uiModule.ExportedFunctions.ContainsKey('Show-Menu') if ($hasShowMenu) { Write-Verbose "Re-exporting Show-Menu function from HomeLab.UI" Export-ModuleMember -Function Show-Menu } else { Write-Warning "Show-Menu function not found in HomeLab.UI module" } } catch { Write-Warning "Error checking for Show-Menu function: $_" } } # Export functions from the main module Export-ModuleMember -Function Start-HomeLab, Start-MainLoop, Initialize-Environment, Initialize-Configuration # Export critical functions that might be needed if (Get-Command -Name Get-AzureConnection -ErrorAction SilentlyContinue) { Export-ModuleMember -Function Get-AzureConnection } if (Get-Command -Name Import-RequiredModules -ErrorAction SilentlyContinue) { Export-ModuleMember -Function Import-RequiredModules } if (Get-Command -Name Test-ModuleAvailability -ErrorAction SilentlyContinue) { Export-ModuleMember -Function Test-ModuleAvailability } if (Get-Command -Name Wait-BeforeSplash -ErrorAction SilentlyContinue) { Export-ModuleMember -Function Wait-BeforeSplash } # Restore automatic module loading $PSModuleAutoLoadingPreference = 'All' # Restore the original PSDefaultParameterValues if ($originalPSDefaultParameterValues) { $global:PSDefaultParameterValues = $originalPSDefaultParameterValues } # Set a flag to indicate that the module has been loaded $script:ModulesLoaded = $true Write-Verbose "HomeLab module initialization complete" |