ImportDependency.psm1
|
#----------------------------------------------- # NOTES #----------------------------------------------- <# Inspired by Tutorial of RamblingCookieMonster in http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/ and https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/PSStackExchange.psm1 #> #----------------------------------------------- # OS CHECK #----------------------------------------------- Write-Verbose "Checking the Core and OS" $preCheckisCore = $PSVersionTable.Keys -contains "PSEdition" -and $PSVersionTable.PSEdition -eq 'Core' # Check the operating system, if Core if ($preCheckisCore -eq $true) { If ( $IsWindows -eq $true ) { $preCheckOs = "Windows" } elseif ( $IsLinux -eq $true ) { $preCheckOs = "Linux" } elseif ( $IsMacOS -eq $true ) { $preCheckOs = "MacOS" } else { throw "Unknown operating system" } } else { $preCheckOs = "Windows" } #----------------------------------------------- # ADD MODULE PATH, IF NOT PRESENT #----------------------------------------------- If ( $preCheckOs -eq "Windows" -and $preCheckisCore -eq $false ) { Write-Verbose "Adding Module path on Windows (when not using Core)" $modulePath = @( [System.Environment]::GetEnvironmentVariable("PSModulePath") -split ";" ) + @( "$( [System.Environment]::GetEnvironmentVariable("ProgramFiles") )\WindowsPowerShell\Modules" "$( [System.Environment]::GetEnvironmentVariable("ProgramFiles(x86)") )\WindowsPowerShell\Modules" "$( [System.Environment]::GetEnvironmentVariable("USERPROFILE") )\Documents\WindowsPowerShell\Modules" "$( [System.Environment]::GetEnvironmentVariable("windir") )\system32\WindowsPowerShell\v1.0\Modules" ) # Add the 64bit path, if present. In 32bit the ProgramFiles variables only returns the x86 path If ( [System.Environment]::GetEnvironmentVariables().keys -contains "ProgramW6432" ) { $modulePath += "$( [System.Environment]::GetEnvironmentVariable("ProgramW6432") )\WindowsPowerShell\Modules" } # Add all paths # Using $env:PSModulePath for only temporary override $Env:PSModulePath = @( $modulePath | Sort-Object -unique ) -join ";" } #----------------------------------------------- # ADD SCRIPT PATH, IF NOT PRESENT #----------------------------------------------- If ( $preCheckOs -eq "Windows" -and $preCheckisCore -eq $false ) { Write-Verbose "Adding Script path on Windows (when not using Core)" #$envVariables = [System.Environment]::GetEnvironmentVariables() $scriptPath = @( [System.Environment]::GetEnvironmentVariable("Path") -split ";" ) + @( "$( [System.Environment]::GetEnvironmentVariable("ProgramFiles") )\WindowsPowerShell\Scripts" "$( [System.Environment]::GetEnvironmentVariable("ProgramFiles(x86)") )\WindowsPowerShell\Scripts" "$( [System.Environment]::GetEnvironmentVariable("USERPROFILE") )\Documents\WindowsPowerShell\Scripts" ) # Add the 64bit path, if present. In 32bit the ProgramFiles variables only returns the x86 path If ( [System.Environment]::GetEnvironmentVariables().keys -contains "ProgramW6432" ) { $scriptPath += "$( [System.Environment]::GetEnvironmentVariable("ProgramW6432") )\WindowsPowerShell\Scripts" } # Using $env:Path for only temporary override $Env:Path = @( $scriptPath | Sort-Object -unique ) -join ";" } #----------------------------------------------- # LOAD PUBLIC AND PRIVATE FUNCTIONS #----------------------------------------------- #$PSBoundParameters["Verbose"].IsPresent -eq $true Write-Verbose "Loading public and private functions" $Public = @( Get-ChildItem -Path "$( $PSScriptRoot )/Public/*.ps1" -ErrorAction SilentlyContinue ) $Private = @( Get-ChildItem -Path "$( $PSScriptRoot )/Private/*.ps1" -ErrorAction SilentlyContinue ) # dot source the files @( $Public + $Private ) | ForEach-Object { $import = $_ Write-Verbose "Load function $( $import.fullname )" #-verbose Try { . $import.fullname } Catch { Write-Error -Message "Failed to import function $( $import.fullname ): $( $_ )" } } #----------------------------------------------- # SET SOME VARIABLES ONLY VISIBLE TO MODULE AND FUNCTIONS #----------------------------------------------- Write-Verbose "Define internal module variables" # Define the variables #New-Variable -Name execPath -Value $null -Scope Script -Force # Path of the calling script New-Variable -Name psVersion -Value $null -Scope Script -Force # PowerShell version being used New-Variable -Name psEdition -Value $null -Scope Script -Force # Edition of PowerShell (e.g., Desktop, Core) New-Variable -Name platform -Value $null -Scope Script -Force # Platform type (e.g., Windows, Linux, macOS) New-Variable -Name frameworkPreference -Value $null -Scope Script -Force # Preferred .NET framework version New-Variable -Name runtimePreference -Value $null -Scope Script -Force # Preferred OS native framework version New-Variable -Name isCore -Value $null -Scope Script -Force # Indicates if PowerShell Core is being used (True/False) New-Variable -Name isCoreInstalled -Value $null -Scope Script -Force # Indicates if PowerShell Core is already installed (True/False) New-Variable -Name defaultPsCoreVersion -Value $null -Scope Script -Force # Default version of PowerShell Core that is used New-Variable -Name defaultPsCoreIs64Bit -Value $null -Scope Script -Force # If default PowerShell is 64-bit (True/False) New-Variable -Name defaultPsCorePath -Value $null -Scope Script -Force # Default Path where PowerShell Core is installed New-Variable -Name os -Value $null -Scope Script -Force # Operating system name New-Variable -Name is64BitOS -Value $null -Scope Script -Force # Indicates if the OS is 64-bit (True/False) New-Variable -Name is64BitProcess -Value $null -Scope Script -Force # Indicates if the process is 64-bit (True/False) New-Variable -Name executingUser -Value $null -Scope Script -Force # User executing the script New-Variable -Name isElevated -Value $null -Scope Script -Force # Indicates if the script is running with elevated privileges (True/False) New-Variable -Name packageManagement -Value $null -Scope Script -Force # Package management system in use (e.g., NuGet, APT) New-Variable -Name powerShellGet -Value $null -Scope Script -Force # Version of PowerShellGet module New-Variable -Name vcredist -Value $null -Scope Script -Force # Indicates if Visual C++ Redistributable is installed (True/False) New-Variable -Name installedModules -Value $null -Scope Script -Force # Caches all installed PowerShell modules New-Variable -Name backgroundJobs -Value $null -Scope Script -Force # Hidden variable to store background jobs New-Variable -Name installedGlobalPackages -Value $null -Scope Script -Force # Caches all installed NuGet Global Packages New-Variable -Name executionPolicy -Value $null -Scope Script -Force # Current execution policy # Filling some default values $Script:isCore = $preCheckisCore $Script:os = $preCheckOs $Script:psVersion = $PSVersionTable.PSVersion.ToString() $Script:powerShellEdition = $PSVersionTable.PSEdition # Need to write that out because psedition is reserved $Script:platform = $PSVersionTable.Platform $Script:is64BitOS = [System.Environment]::Is64BitOperatingSystem $Script:is64BitProcess = [System.Environment]::Is64BitProcess $Script:executionPolicy = Get-ExecutionPolicy -Scope MachinePolicy Write-Verbose "Checking more details about PS Core" # Check if pscore is installed $pwshCommand = Get-Command -commandType Application -Name "pwsh*" $Script:defaultPsCoreVersion = $pwshCommand[0].Version If ( $pwshCommand.Count -gt 0 ) { $Script:isCoreInstalled = $true if ($Script:os -eq "Windows") { # For Windows $Script:defaultPsCorePath = ( get-command -name "pwsh*" -CommandType Application | where-object { $_.Source.replace("\pwsh.exe","") -eq ( pwsh { $pshome } ) } ).Source } elseif ( $Script:os -eq "Linux" ) { # For Linux If ( $null -ne (which pwse) ) { $Script:defaultPsCorePath = (which pwse) } } } else { $Script:isCoreInstalled = $false } Write-Verbose "Checking the processor architecture" # Checking the processor architecture and operating system architecture If ( $null -ne [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture ) { Switch ( [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().toUpper() ) { 'X64' { $arch = 'x64' } 'X86' { $arch = 'x32' } 'ARM64' { $arch = 'arm64' } 'ARM' { $arch = 'arm' } Default { $arch = 'Unknown' } } } else { # Used code from: https://gist.github.com/asheroto/cfa26dd00177a03c81635ea774406b2b # Get OS details using Get-CimInstance because the registry key for Name is not always correct with Windows 11 $osDetails = Get-CimInstance -ClassName Win32_OperatingSystem # Get architecture details of the OS (not the processor) $arch = $osDetails.OSArchitecture } # Normalize architecture if ($arch -match "(?i)32") { $Script:architecture = "x32" } elseif ($arch -match "(?i)64" -and $arch -match "(?i)ARM") { $Script:architecture = "ARM64" } elseif ($arch -match "(?i)64") { $Script:architecture = "x64" } elseif ($arch -match "(?i)ARM") { $Script:architecture = "ARM" } else { $Script:architecture = "Unknown" } Write-Verbose "Checking the .NET package runtime preference order" # Check which runtimes to prefer $Script:runtimePreference = @() switch ($Script:os) { 'Windows'{ If ($Script:architecture -eq "ARM64") { $Script:runtimePreference = @( "win-arm64", "win-arm", "win-x64" ) } If ($Script:architecture -eq "ARM") { $Script:runtimePreference = @( "win-arm" ) } If ($Script:architecture -eq "x64") { $Script:runtimePreference = @( "win-x64" ) } $Script:runtimePreference += @( "win-x86" ) $Script:runtimePreference += @( "win" ) } 'Linux' { If ($Script:architecture -eq "ARM64") { $Script:runtimePreference = @( "linux-arm64", "linux-arm", "linux-x64" ) } If ($Script:architecture -eq "ARM") { $Script:runtimePreference = @( "linux-arm" ) } If ($Script:architecture -eq "x64") { $Script:runtimePreference = @( "linux-x64" ) } $Script:runtimePreference += @( "linux-x86" ) } 'MacOS' { If ($Script:architecture -eq "ARM64") { $Script:runtimePreference = @( "osx-arm64" ) } If ($Script:architecture -eq "x64") { $Script:runtimePreference = @( "osx-x64" ) } } default { throw "Unsupported OS: $os" } } Write-Verbose "Checking the .NET package lib preference order" # Check lib preference $Script:frameworkPreference = @() $ver = [System.Environment]::Version if ( $PSVersionTable.PSEdition -eq 'Desktop' ) { # Desktop PowerShell can load any net4x up to the installed version $maxFramework = switch ($ver.Major) { 4 { "net48" } # most common Windows PowerShell 5.1 runs on .NET 4.8 default { "net48" } } # Add net4x folders descending from the max version $net4x = @('net48','net471','net47','net462','net461','net45','net40') $Script:frameworkPreference += $net4x[($net4x.IndexOf($maxFramework))..($net4x.Count-1)] # Then add netstandard (2.0 is the highest fully supported on .NET 4.8) $Script:frameworkPreference += 'netstandard2.0','netstandard1.5','netstandard1.3','netstandard1.1','netstandard1.0' } else { # PowerShell 7+ runs on .NET 6, 7, or 8 – pick the highest available $major = $ver.Major # 6,7,8 … $minor = $ver.Minor # usually 0 # Add the exact netX.Y folder first $Script:frameworkPreference += "net$( $major ).$( $minor )" # Add newer “windows” variants if they exist $Script:frameworkPreference += "net$( $major ).$( $minor )-windows" # Add previous major versions for ($m = $major-1; $m -ge 5; $m--) { $Script:frameworkPreference += "net$( $m ).0" $Script:frameworkPreference += "net$( $m ).0-windows" } # Finally netstandard fall‑back $Script:frameworkPreference += 'netcoreapp2.0','netstandard2.1','netstandard2.0','netstandard1.5','netstandard1.3','netstandard1.1','netstandard1.0' } Write-Verbose "Checking Elevation" # Check elevation # TODO check for MacOS if ($Script:os -eq "Windows") { $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $Script:executingUser = $identity.Name $principal = [Security.Principal.WindowsPrincipal]::new($identity) $Script:isElevated = $principal.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) } elseif ( $Script:os -eq "Linux" ) { $Script:executingUser = whoami $Script:isElevated = -not [String]::IsNullOrEmpty($env:SUDO_USER) } Write-Verbose "Checking PackageManagement and PowerShellGet versions" # Check if PackageManagement and PowerShellGet are available $Script:packageManagement = ( Get-Module -Name "PackageManagement" -ListAvailable -ErrorAction SilentlyContinue | Sort-Object Version -Descending | Select-Object -First 1 ).Version.toString() $Script:powerShellGet = ( Get-Module -Name "PowerShellGet" -ListAvailable -ErrorAction SilentlyContinue | Sort-Object Version -Descending | Select-Object -First 1 ).Version.toString() Write-Verbose "Add background jobs to work out the installed modules and packages" # Add jobs to find out more about installed modules and packages in the background # TODO add in multiple paths for pscore ? $Script:backgroundJobs = [System.Collections.ArrayList]@() If ( $Script:isCoreInstalled -eq $True ) { [void]$Script:backgroundJobs.Add(( Start-Job -ScriptBlock { pwsh { [System.Environment]::Is64BitProcess } } -Name "PwshIs64Bit" )) } [void]$Script:backgroundJobs.Add(( Start-Job -ScriptBlock { $modules = [System.Collections.ArrayList]@() Get-Module -ListAvailable | Where-Object { $_.ModuleType -ne "Manifest" } | Select-Object Name, Path, ModuleType, Version, PreRelease, NestedModules, ExportedFunctions, ExportedCmdlets, ExportedVariables, ExportedAliases | Group-Object Name | ForEach-Object { $modules.Add(( $_.Group | Sort-Object Version -Descending | Select-Object -First 1 )) | Out-Null } $modules } -Name "InstalledModule" )) [void]$Script:backgroundJobs.Add(( Start-Job -ScriptBlock { param($ModuleRoot, $OS) # Load the needed assemblies Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop # Paths are dependent on the os if ($OS -eq "Windows") { $pathsToCheck = @( ( Join-Path $env:USERPROFILE ".nuget\packages" ) "$( [System.Environment]::GetEnvironmentVariable("ProgramFiles") )\PackageManagement\NuGet\Packages" "$( [System.Environment]::GetEnvironmentVariable("ProgramFiles(x86)") )\PackageManagement\NuGet\Packages" ) } else { $pathsToCheck = @( ( Join-Path $HOME ".nuget/packages" ) ) } # Dot source the needed function . ( Join-Path $ModuleRoot "/Public/Get-LocalPackage.ps1" ) # Load the packages $packages = Get-LocalPackage -NugetRoot $pathsToCheck $packages } -Name "InstalledGlobalPackages" -ArgumentList $PSScriptRoot.ToString(), $preCheckOs )) Write-Verbose "Checking VCRedist" # Check the vcredist installation $vcredistInstalled = $False $vcredist64 = $False $vcRedistCollection = $null # Possible registry paths for Visual C++ Redistributable installations If ( $Script:os -eq "Windows" ) { try { # Attempt to retrieve the Visual C++ Redistributable 14 registry entry $vcReg = [Array]@( Get-ItemProperty 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\*\VC\Runtimes\*' -ErrorAction Stop ) If ( $vcReg.Count -gt 0 ) { $vcredistInstalled = $True $vcRedistCollection = [hashtable]@{} $vcReg | ForEach-Object { $vcRegItem = $_ If ( $vcRegItem.PSChildName -like "*64" -and $vcRegItem.Installed -gt 0 ) { $vcredist64 = $True } $vcRedistCollection.Add($vcRegItem.PSChildName, ([PSCustomObject]@{ "Version" = $vcRegItem.Version "Major" = $vcRegItem.Major "Minor" = $vcRegItem.Minor "Build" = $vcRegItem.Build "Installed" = $vcRegItem.Installed } ) ) } } } catch { Write-Verbose "VCRedist is not installed" } } $Script:vcredist = [PSCustomObject]@{ "installed" = $vcredistInstalled "is64bit" = $vcredist64 "versions" = $vcRedistCollection } #----------------------------------------------- # MAKE PUBLIC FUNCTIONS PUBLIC #----------------------------------------------- Write-Verbose "Exporting public functions" Export-ModuleMember -Function $Public.Basename #-verbose #+ "Set-Logfile" |