function Repair-PowerShellGetStack { <# .SYNOPSIS Repairs (self-heals) the local PowerShellGet/PackageManagement installation so PowerShellGet v2 cmdlets (e.g. Get-PSRepository) are available. .DESCRIPTION Normalizes common broken module layouts for PowerShellGet and PackageManagement that prevent Import-Module PowerShellGet from working. This can happen when the modules are installed/unpacked into non-standard folder names such as: - PowerShellGet\PowerShellGet.2.2.5\ - PowerShellGet\\ - PackageManagement\PackageManagement.1.4.8.1\ - PowerShellGet\PackageManagement.1.4.8.1\ (wrong root) The function: 1) Ensures the Program Files module path is present in $env:PSModulePath 2) Moves PackageManagement out of the wrong root if found under the PowerShellGet folder 3) Renames/moves module folders into the standard versioned layout: ...\Modules\PackageManagement\\ ...\Modules\PowerShellGet\\ 4) Imports the requested versions and verifies Get-PSRepository is available Safe to run multiple times. It does not download anything from the internet; it only rearranges/loads locally present modules. .PARAMETER PowerShellGetVersion The PowerShellGet version to normalize and import (default: 2.2.5). .PARAMETER PackageManagementVersion The PackageManagement version to normalize and import (default: 1.4.8.1). #> [CmdletBinding()] param( [Version]$PowerShellGetVersion = [Version]'2.2.5', [Version]$PackageManagementVersion= [Version]'1.4.8.1' ) function _EnsurePSModulePath { $all = Join-Path $env:ProgramFiles 'WindowsPowerShell\Modules' if (($env:PSModulePath -split ';') -notcontains $all) { $env:PSModulePath = "$env:PSModulePath;$all" } } function _NormalizeModuleFolder { param( [Parameter(Mandatory)][string]$ModuleName, [Parameter(Mandatory)][Version]$Version ) $base = Join-Path (Join-Path $env:ProgramFiles 'WindowsPowerShell\Modules') $ModuleName New-Item -ItemType Directory -Path $base -Force | Out-Null $wanted = Join-Path $base $Version.ToString() # common wrong names: $candidates = @( Join-Path $base ("{0}.{1}" -f $ModuleName, $Version.ToString()), Join-Path $base ("{0}.{1}" -f $ModuleName, $Version.ToString()), Join-Path $base ("{0}.{1}" -f $ModuleName, $Version.ToString(3)) ) | Select-Object -Unique $manifestName = "{0}.psd1" -f $ModuleName $foundManifests = Get-ChildItem -Path $base -Recurse -Filter $manifestName -ErrorAction SilentlyContinue foreach ($m in $foundManifests) { $candidates += (Split-Path $m.FullName -Parent) } $candidates = $candidates | Select-Object -Unique if (Test-Path $wanted) { return } foreach ($src in $candidates) { if (-not (Test-Path $src)) { continue } if (-not (Test-Path (Join-Path $src $manifestName))) { continue } try { if ($src -eq $wanted) { return } if (Test-Path $wanted) { Remove-Item -Recurse -Force $wanted } Move-Item -Path $src -Destination $wanted -Force return } catch { try { if (Test-Path $wanted) { Remove-Item -Recurse -Force $wanted } Copy-Item -Path $src -Destination $wanted -Recurse -Force return } catch { } } } } _EnsurePSModulePath $pmWrong = Join-Path (Join-Path (Join-Path $env:ProgramFiles 'WindowsPowerShell\Modules') 'PowerShellGet') ("PackageManagement.{0}" -f $PackageManagementVersion.ToString()) if (Test-Path $pmWrong) { $pmRightBase = Join-Path (Join-Path $env:ProgramFiles 'WindowsPowerShell\Modules') 'PackageManagement' New-Item -ItemType Directory -Path $pmRightBase -Force | Out-Null $pmRight = Join-Path $pmRightBase $PackageManagementVersion.ToString() if (Test-Path $pmRight) { Remove-Item -Recurse -Force $pmRight } try { Move-Item -Path $pmWrong -Destination $pmRight -Force } catch { Copy-Item -Path $pmWrong -Destination $pmRight -Recurse -Force } } _NormalizeModuleFolder -ModuleName 'PackageManagement' -Version $PackageManagementVersion _NormalizeModuleFolder -ModuleName 'PowerShellGet' -Version $PowerShellGetVersion Remove-Module PackageManagement,PowerShellGet -Force -ErrorAction SilentlyContinue Import-Module PackageManagement -RequiredVersion $PackageManagementVersion -Force -ErrorAction Stop Import-Module PowerShellGet -RequiredVersion $PowerShellGetVersion -Force -ErrorAction Stop if (-not (Get-Command Get-PSRepository -ErrorAction SilentlyContinue)) { throw "PowerShellGet loaded but Get-PSRepository still missing." } return $true } function Install-SVSMSP { [CmdletBinding()] param ( [switch] $Cleanup, [switch] $InstallToolkit, [Parameter(Mandatory = $false)] [array] $AllModules = @(@{ ModuleName = "SVS_Toolkit" }, @{ ModuleName = "SVSMSP" }), [Parameter(Mandatory = $false)] [array] $AllRepositories = @(@{ RepoName = "SVS_Repo" }, @{ RepoName = "SVS_Toolkit" }), [Parameter(Mandatory = $false)] [string] $NewModuleName = "SVSMSP", [Parameter(Mandatory = $false)] [string] $NewRepositoryName = "SVS_Repo", # Legacy PSGet v2 URL (works with Find-Package/Install-Package; PSGet v2 repo registration may fail on some machines) [Parameter(Mandatory = $false)] [string] $NewRepositoryURL = "https://proget.svstools.ca/nuget/SVS_Repo/", # PSResourceGet V3 service index URL (this is what made your install work) [Parameter(Mandatory = $false)] [string] $NewRepositoryV3Url = "https://proget.svstools.ca/nuget/SVS_Repo/v3/index.json" ) function Ensure-Tls12 { try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch { } } function Ensure-NuGetProvider { Ensure-Tls12 try { if (-not (Get-PackageProvider -ListAvailable -Name NuGet -ErrorAction SilentlyContinue)) { Install-PackageProvider -Name NuGet -Force -Scope AllUsers | Out-Null } } catch { Write-LogHybrid "Failed to ensure NuGet provider: $($_.Exception.Message)" "Warning" "SVSModule" -LogToEvent } } function Ensure-PSResourceGet { Ensure-NuGetProvider try { if (-not (Get-Module Microsoft.PowerShell.PSResourceGet -ListAvailable -ErrorAction SilentlyContinue)) { Install-Module Microsoft.PowerShell.PSResourceGet -Scope AllUsers -Force -AllowClobber -ErrorAction Stop } Import-Module Microsoft.PowerShell.PSResourceGet -Force -ErrorAction Stop return $true } catch { Write-LogHybrid "PSResourceGet not available: $($_.Exception.Message)" "Warning" "SVSModule" -LogToEvent return $false } } function Ensure-PowerShellGetV2 { Ensure-NuGetProvider try { Remove-Module PowerShellGet,PackageManagement -Force -ErrorAction SilentlyContinue # Prefer modern versions (avoid PowerShellGet 1.0.0.1) Import-Module PackageManagement -RequiredVersion 1.4.8.1 -Force -ErrorAction Stop Import-Module PowerShellGet -RequiredVersion 2.2.5 -Force -ErrorAction Stop return $true } catch { Write-LogHybrid "Failed to load required PowerShellGet/PackageManagement versions: $($_.Exception.Message)" "Warning" "SVSModule" -LogToEvent return $false } } function Get-InstallStrategy { # 1) PSResourceGet if present/usable if (Ensure-PSResourceGet) { return "PSResourceGet" } # 2) Only use PSGet v2 if we can force-load the good versions if (Ensure-PowerShellGetV2) { return "PowerShellGetV2" } # 3) NuGet provider fallback return "NuGetProvider" } function Import-InstalledModuleOrThrow { param([Parameter(Mandatory=$true)][string]$Name) try { Import-Module $Name -Force -ErrorAction Stop } catch { Write-LogHybrid "Module import failed for '$Name': $($_.Exception.Message)" "Error" "SVSModule" -LogToEvent throw } } function Register-Repo_PSResourceGet { Write-LogHybrid "Registering PSResourceGet repo $NewRepositoryName…" "Info" "SVSModule" -LogToEvent if (-not (Get-PSResourceRepository -Name $NewRepositoryName -ErrorAction SilentlyContinue)) { Register-PSResourceRepository -Name $NewRepositoryName -Uri $NewRepositoryV3Url -Trusted -ErrorAction Stop } } function Install-Module_PSResourceGet { Write-LogHybrid "Installing module $NewModuleName via PSResourceGet…" "Info" "SVSModule" -LogToEvent Install-PSResource -Name $NewModuleName -Repository $NewRepositoryName -Scope AllUsers -Reinstall -ErrorAction Stop Import-InstalledModuleOrThrow -Name $NewModuleName } function Register-Repo_PowerShellGetV2 { Write-LogHybrid "Registering PSRepository $NewRepositoryName…" "Info" "SVSModule" -LogToEvent if (-not (Get-PSRepository -Name $NewRepositoryName -ErrorAction SilentlyContinue)) { Register-PSRepository -Name $NewRepositoryName -SourceLocation $NewRepositoryURL -InstallationPolicy Trusted -ErrorAction Stop } } function Install-Module_PowerShellGetV2 { Write-LogHybrid "Installing module $NewModuleName via PowerShellGet…" "Info" "SVSModule" -LogToEvent Install-Module -Name $NewModuleName -Repository $NewRepositoryName -Scope AllUsers -Force -ErrorAction Stop Import-InstalledModuleOrThrow -Name $NewModuleName } function Register-Repo_NuGetProvider { Ensure-NuGetProvider Write-LogHybrid "Registering PackageSource $NewRepositoryName…" "Info" "SVSModule" -LogToEvent try { Register-PackageSource -Name $NewRepositoryName -ProviderName NuGet -Location $NewRepositoryURL -Trusted -Force -ErrorAction Stop | Out-Null } catch { Write-LogHybrid "Failed to register PackageSource '$NewRepositoryName': $($_.Exception.Message)" "Error" "SVSModule" -LogToEvent throw } } function Install-Module_NuGetProvider { Write-LogHybrid "Installing package $NewModuleName via NuGet provider…" "Info" "SVSModule" -LogToEvent try { Install-Package -Name $NewModuleName -Source $NewRepositoryName -Force -ErrorAction Stop | Out-Null } catch { Write-LogHybrid "Failed Install-Package '$NewModuleName': $($_.Exception.Message)" "Error" "SVSModule" -LogToEvent throw } # Optional: promote .nupkg to Modules path so Import-Module works like a normal module try { $pkg = Get-Package -Name $NewModuleName -ProviderName NuGet -ErrorAction Stop | Select-Object -First 1 $nupkg = $pkg.Source if (-not (Test-Path $nupkg)) { return } $stage = Join-Path $env:TEMP ("{0}_{1}" -f $NewModuleName, $pkg.Version) Remove-Item -Recurse -Force $stage -ErrorAction SilentlyContinue Expand-Archive -Path $nupkg -DestinationPath $stage -Force $psd1 = Get-ChildItem -Path $stage -Recurse -Filter ($NewModuleName + ".psd1") -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $psd1) { return } $moduleRoot = Split-Path $psd1.FullName -Parent $dest = Join-Path $env:ProgramFiles ("WindowsPowerShell\Modules\{0}\{1}" -f $NewModuleName, $pkg.Version) New-Item -ItemType Directory -Path $dest -Force | Out-Null Copy-Item -Path (Join-Path $moduleRoot '*') -Destination $dest -Recurse -Force Import-InstalledModuleOrThrow -Name $NewModuleName } catch { Write-LogHybrid "NuGet promotion step skipped/failed: $($_.Exception.Message)" "Warning" "SVSModule" -LogToEvent } } function Start-Cleanup { Write-LogHybrid "Cleanup mode enabled. Starting cleanup..." "Info" "SVSModule" # Self-heal PowerShellGet only on systems missing PSGet v2 cmdlets if (-not (Get-Command Get-PSRepository -ErrorAction SilentlyContinue)) { try { Repair-PowerShellGetStack | Out-Null } catch { Write-LogHybrid "PowerShellGet self-heal failed: $($_.Exception.Message). Skipping PSGet v2 repo operations." "Warning" "SVSModule" -LogToEvent } } # Uninstall module using whichever tooling exists try { if (Get-Command Uninstall-PSResource -ErrorAction SilentlyContinue) { Uninstall-PSResource -Name $NewModuleName -Scope AllUsers -ErrorAction SilentlyContinue } } catch { } try { Uninstall-Module -Name $NewModuleName -AllVersions -Force -ErrorAction SilentlyContinue } catch { } try { Uninstall-Package -Name $NewModuleName -ProviderName NuGet -AllVersions -Force -ErrorAction SilentlyContinue | Out-Null } catch { } # PSGet v2 repo cleanup (guarded) if (Get-Command Get-PSRepository -ErrorAction SilentlyContinue) { if (Get-PSRepository -Name $NewRepositoryName -ErrorAction SilentlyContinue) { try { Unregister-PSRepository -Name $NewRepositoryName -ErrorAction Stop Write-LogHybrid "$NewRepositoryName repository unregistered (PSGet v2)." "Success" "SVSModule" -LogToEvent } catch { Write-LogHybrid "Failed to unregister PSRepository ${NewRepositoryName}: $($_.Exception.Message)" "Error" "SVSModule" -LogToEvent } } } else { Write-LogHybrid "Skipping PSGet v2 repo cleanup: Get-PSRepository not available on this system." "Info" "SVSModule" -LogToEvent } # PSResourceGet repo cleanup (already guarded) if (Get-Command Get-PSResourceRepository -ErrorAction SilentlyContinue) { if (Get-PSResourceRepository -Name $NewRepositoryName -ErrorAction SilentlyContinue) { try { Unregister-PSResourceRepository -Name $NewRepositoryName -ErrorAction Stop Write-LogHybrid "$NewRepositoryName repository unregistered (PSResourceGet)." "Success" "SVSModule" -LogToEvent } catch { Write-LogHybrid "Failed to unregister PSResource repo ${NewRepositoryName}: $($_.Exception.Message)" "Error" "SVSModule" -LogToEvent } } } # NuGet provider source cleanup (guarded by Get-PackageSource existence) if (Get-PackageSource -Name $NewRepositoryName -ErrorAction SilentlyContinue) { try { Unregister-PackageSource -Name $NewRepositoryName -ErrorAction SilentlyContinue | Out-Null Write-LogHybrid "$NewRepositoryName package source unregistered (NuGet provider)." "Success" "SVSModule" -LogToEvent } catch { } } if (Get-Module -Name $NewModuleName) { try { Remove-Module $NewModuleName -Force -ErrorAction Stop Write-LogHybrid "$NewModuleName module removed from current session." "Success" "SVSModule" -LogToEvent } catch { Write-LogHybrid "Failed to remove $NewModuleName from session: $($_.Exception.Message)" "Error" "SVSModule" -LogToEvent } } $cscePath = 'C:\CSCE' if (Test-Path $cscePath) { try { Remove-Item -Path $cscePath -Recurse -Force Write-LogHybrid "Deleted '$cscePath' contents." "Success" "SVSModule" -LogToEvent } catch { Write-LogHybrid "Failed to delete '$cscePath': $($_.Exception.Message)" "Warning" "SVSModule" -LogToEvent } } } function Remove-SVSDeploymentRegKey { $regKey = 'HKLM:\Software\SVS' try { if (Test-Path $regKey) { Remove-Item -Path $regKey -Recurse -Force Write-LogHybrid "Registry key '$regKey' deleted successfully." "Success" "SVSModule" -LogToEvent } else { Write-LogHybrid "Registry key '$regKey' not found; nothing to delete." "Info" "SVSModule" -LogToEvent } } catch { Write-LogHybrid "Failed to delete registry key '$regKey': $($_.Exception.Message)" "Error" "SVSModule" -LogToEvent } } function Repair-SVSMspEventLogBinding { param( [string]$EventSource = "SVSMSP_Module", [string]$TargetLog = "SVSMSP Events" ) Write-LogHybrid "Checking Event Log binding for source '$EventSource'..." Info SVSModule -LogToEvent try { if (-not [System.Diagnostics.EventLog]::SourceExists($EventSource)) { Write-LogHybrid "Event source '$EventSource' not found. Nothing to repair." Info SVSModule -LogToEvent return } $currentLog = [System.Diagnostics.EventLog]::LogNameFromSourceName($EventSource, '.') } catch { Write-LogHybrid "Failed to query Event Log binding for '$EventSource': $($_.Exception.Message)" Warning SVSModule -LogToEvent return } if (-not $currentLog) { Write-LogHybrid "Could not determine current log for event source '$EventSource'. Skipping repair." Warning SVSModule -LogToEvent return } if ($currentLog -eq $TargetLog) { Write-LogHybrid "Event source '$EventSource' already bound to '$TargetLog'." Info SVSModule -LogToEvent return } Write-LogHybrid "Rebinding event source '$EventSource' from '$currentLog' to '$TargetLog'..." Warning SVSModule -LogToEvent try { [System.Diagnostics.EventLog]::DeleteEventSource($EventSource) New-EventLog -LogName $TargetLog -Source $EventSource -ErrorAction Stop Write-LogHybrid "Event source '$EventSource' rebound to '$TargetLog'." Success SVSModule -LogToEvent } catch { Write-LogHybrid "Failed to rebind event source '$EventSource' to log '$TargetLog': $($_.Exception.Message)" Error SVSModule -LogToEvent } } function Start-ToolkitInstallation { Start-Cleanup $strategy = Get-InstallStrategy Write-LogHybrid "Selected install strategy: $strategy" "Info" "SVSModule" -LogToEvent try { switch ($strategy) { "PSResourceGet" { Register-Repo_PSResourceGet Install-Module_PSResourceGet } "PowerShellGetV2" { if (-not (Ensure-PowerShellGetV2)) { throw "PowerShellGetV2 not usable on this system." } Register-Repo_PowerShellGetV2 Install-Module_PowerShellGetV2 } default { Register-Repo_NuGetProvider Install-Module_NuGetProvider } } } catch { Write-LogHybrid "Installation failed using ${strategy}: $($_.Exception.Message)" "Error" "SVSModule" -LogToEvent throw } Repair-SVSMspEventLogBinding -EventSource "SVSMSP_Module" -TargetLog "SVSMSP Events" Write-LogHybrid "Toolkit installation completed." "Success" "SVSModule" -LogToEvent } Write-LogHybrid "Install-SVSMSP called" "Info" "SVSModule" -LogToEvent if ($Cleanup) { Start-Cleanup Remove-SVSDeploymentRegKey return } if ($InstallToolkit) { Start-ToolkitInstallation; return } Start-ToolkitInstallation }