packages/AzStackHci.DiagnosticSettings.0.6.8/Public/AzStackHci.PageFileSettings.ps1
|
# /////////////////////////////////////////////////////////////////// # Strict Mode v1 (PS 5.1 safe) - surfaces reads of uninitialised variables at runtime. Set-StrictMode -Version 1.0 # Get-AzStackHciPageFileSettings Function # Used to get information about the current page file settings # on the system. # /////////////////////////////////////////////////////////////////// Function Get-AzStackHciPageFileSettings { <# .SYNOPSIS Gets current page file settings .DESCRIPTION Queries the system for current page file settings. Use -Scope Cluster to collect settings from all nodes in the failover cluster. #> [CmdletBinding()] [OutputType([PSCustomObject[]])] param ( # Scope: 'Node' (default) for local node only, 'Cluster' to collect from all cluster nodes [Parameter(Mandatory=$false)] [ValidateSet('Node','Cluster')] [string]$Scope = 'Node', [switch]$NoOutput ) begin { # Requires administrator permissions to get page file settings and write to log file to disk if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } $DateFormatted = Get-Date -f "yyyyMMdd-HHmm" if(-not(Test-Path "C:\ProgramData\AzStackHci.DiagnosticSettings\")) { try { New-Item "C:\ProgramData\AzStackHci.DiagnosticSettings\" -ItemType Directory -ErrorAction Stop | Out-Null } catch { Throw "Failed to create output directory 'C:\ProgramData\AzStackHci.DiagnosticSettings\': $($_.Exception.Message)" } } } process { # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active $script:SilentMode = $false # Handle -NoOutput: suppress all console output if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # --------------------------------------------------------------- # Cluster scope: fan out to all cluster nodes via Invoke-Command # --------------------------------------------------------------- if ($Scope -eq 'Cluster') { if (-not (Test-CommandExists Get-Cluster)) { throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node." } try { [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop } catch { throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)" } Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose # Self-contained scriptblock — does NOT depend on the module being installed on remote nodes $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock { $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Error = $null } try { $DateFormatted = Get-Date -f 'yyyyMMdd-HHmm' $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' if (-not (Test-Path $BackupDir)) { New-Item $BackupDir -ItemType Directory -Force | Out-Null } $BackupFile = "$BackupDir\PageFile_Settings_$DateFormatted.json" [bool]$PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile $PageFileConfig = $null if ($PageFileAutoManaged -eq $false) { $pfSetting = Get-CimInstance -ClassName Win32_PageFileSetting if ($pfSetting) { $PageFileConfig = @{ Name = $pfSetting.Name InitialSize = $pfSetting.InitialSize MaximumSize = $pfSetting.MaximumSize Caption = $pfSetting.Caption Description = $pfSetting.Description SettingID = $pfSetting.SettingID } } } $PageFileUsage = Get-CimInstance Win32_PageFileUsage $backupObject = [PSCustomObject]@{ BackupDate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Node = $env:COMPUTERNAME PageFileAutoManaged = $PageFileAutoManaged PageFileConfig = $PageFileConfig PageFileUsage = @{ AllocatedBaseSize = $PageFileUsage.AllocatedBaseSize CurrentUsage = $PageFileUsage.CurrentUsage PeakUsage = $PageFileUsage.PeakUsage Name = $PageFileUsage.Name } } [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false))) $result.Success = $true $result | Add-Member -NotePropertyName PageFileAutoManaged -NotePropertyValue $PageFileAutoManaged $result | Add-Member -NotePropertyName PageFileName -NotePropertyValue $(if ($PageFileConfig) { $PageFileConfig.Name } else { $null }) $result | Add-Member -NotePropertyName PageFileInitialSize -NotePropertyValue $(if ($PageFileConfig) { $PageFileConfig.InitialSize } else { $null }) $result | Add-Member -NotePropertyName PageFileMaximumSize -NotePropertyValue $(if ($PageFileConfig) { $PageFileConfig.MaximumSize } else { $null }) $result | Add-Member -NotePropertyName PageFileAllocatedBaseSize -NotePropertyValue $PageFileUsage.AllocatedBaseSize $result | Add-Member -NotePropertyName PageFileCurrentUsage -NotePropertyValue $PageFileUsage.CurrentUsage $result | Add-Member -NotePropertyName PageFilePeakUsage -NotePropertyValue $PageFileUsage.PeakUsage } catch { $result.Error = $_.Exception.Message } $result } -ErrorAction SilentlyContinue -ErrorVariable remoteErrors # Report per-node results if ($remoteErrors) { foreach ($err in $remoteErrors) { Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose } } $successCount = 0 $failCount = 0 foreach ($nodeResult in $remoteResults) { if ($nodeResult.Success) { Write-Verbose " $($nodeResult.Node): SUCCESS - backup file created" -Verbose $successCount++ } else { Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)" -Verbose $failCount++ } } Write-Verbose "Cluster collection complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose if ($failCount -gt 0) { Write-Warning "$failCount node(s) failed. Review the output above for details." } # Build PSCustomObject array with settings from all nodes $outputObjects = foreach ($nodeResult in $remoteResults) { [PSCustomObject]@{ Node = $nodeResult.Node Success = $nodeResult.Success PageFileAutoManaged = $nodeResult.PageFileAutoManaged PageFileName = $nodeResult.PageFileName PageFileInitialSize = $nodeResult.PageFileInitialSize PageFileMaximumSize = $nodeResult.PageFileMaximumSize PageFileAllocatedBaseSize = $nodeResult.PageFileAllocatedBaseSize PageFileCurrentUsage = $nodeResult.PageFileCurrentUsage PageFilePeakUsage = $nodeResult.PageFilePeakUsage Error = $nodeResult.Error } } return $outputObjects } try { # Get current page file settings [bool]$script:PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile } catch { Write-Verbose "Failed to get current page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Failed to get current page file settings. Error: $($_.Exception.Message)" } # Collect page file configuration $pfConfig = $null if (-not $script:PageFileAutoManaged) { Write-Verbose "Current Settings: Page File automatic management is Disabled" -Verbose try { $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting } catch { throw "Failed to get page file settings: $($_.Exception.Message)" } if ($script:PageFileConfiguration) { $pfConfig = @{ Name = $script:PageFileConfiguration.Name InitialSize = $script:PageFileConfiguration.InitialSize MaximumSize = $script:PageFileConfiguration.MaximumSize Caption = $script:PageFileConfiguration.Caption Description = $script:PageFileConfiguration.Description SettingID = $script:PageFileConfiguration.SettingID } Write-Verbose " Name: $($pfConfig.Name), InitialSize: $($pfConfig.InitialSize), MaximumSize: $($pfConfig.MaximumSize)" -Verbose } } else { Write-Verbose "Current Settings: Page File automatic management is Enabled" -Verbose } # Collect page file usage try { $script:PageFileUsage = Get-CimInstance Win32_PageFileUsage } catch { throw "Failed to get page file usage: $($_.Exception.Message)" } $script:PageFileAllocatedBaseSize = $PageFileUsage.AllocatedBaseSize $script:PageFileCurrentUsage = $PageFileUsage.CurrentUsage $script:PageFilePeakUsage = $PageFileUsage.PeakUsage Write-Verbose "Page File usage: AllocatedBaseSize=$($script:PageFileAllocatedBaseSize), CurrentUsage=$($script:PageFileCurrentUsage), PeakUsage=$($script:PageFilePeakUsage)" -Verbose # Save backup as JSON $backupObject = [PSCustomObject]@{ BackupDate = (Get-Date -Format "yyyy-MM-dd HH:mm:ss") Node = $env:COMPUTERNAME PageFileAutoManaged = $script:PageFileAutoManaged PageFileConfig = $pfConfig PageFileUsage = @{ AllocatedBaseSize = $PageFileUsage.AllocatedBaseSize CurrentUsage = $PageFileUsage.CurrentUsage PeakUsage = $PageFileUsage.PeakUsage Name = $PageFileUsage.Name } } $BackupFile = "C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_$DateFormatted.json" [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false))) Write-Verbose "Current Page File settings exported to '$BackupFile'" -Verbose Write-HostAzS `n # Return a PSCustomObject for easy reporting [PSCustomObject]@{ Node = $env:COMPUTERNAME Success = $true PageFileAutoManaged = $script:PageFileAutoManaged PageFileName = if ($script:PageFileConfiguration) { $script:PageFileConfiguration.Name } else { $null } PageFileInitialSize = if ($script:PageFileConfiguration) { $script:PageFileConfiguration.InitialSize } else { $null } PageFileMaximumSize = if ($script:PageFileConfiguration) { $script:PageFileConfiguration.MaximumSize } else { $null } PageFileAllocatedBaseSize = $script:PageFileAllocatedBaseSize PageFileCurrentUsage = $script:PageFileCurrentUsage PageFilePeakUsage = $script:PageFilePeakUsage Error = $null } } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Get-AzStackHciPageFileSettings function" } } # End of Get-AzStackHciPageFileSettings # /////////////////////////////////////////////////////////////////// # Set-AzStackHciPageFileSettings Function # Used to set a manual page file on the system with a specified size, # or auto-sized for kernel dump support. # /////////////////////////////////////////////////////////////////// Function Set-AzStackHciPageFileSettings { <# .SYNOPSIS Sets page file settings to a specified size or auto-sizes for kernel dump support .DESCRIPTION Configures a fixed-size manual page file, disabling automatic page file management. Use -KernelDumpRecommended to auto-size the page file based on system memory, using the same calculation as Set-AzStackHciMemoryDumpSettings (64 GiB for <768 GB RAM, 128 GiB for >=768 GB RAM). This ensures the page file is large enough to stage a kernel memory dump. Or specify -InitialSizeMB and -MaximumSizeMB for explicit sizing. Use -Scope Cluster to apply settings to all nodes in the failover cluster via Invoke-Command. The scriptblock is self-contained and does not require the module on remote nodes. Reference: https://learn.microsoft.com/en-us/windows-server/administration/server-core/server-core-memory-dump #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([PSCustomObject[]])] param ( # Path to drive letter for PageFile [Parameter(Mandatory=$true, Position=1)] [ValidateScript({Test-Path $_})] [String] [ValidatePattern('^[C-Zc-z]:\\?$')]$PageFileFileDriveLetter, # Auto-size for kernel dump support (uses same calculation as Set-AzStackHciMemoryDumpSettings) [Parameter(Mandatory=$true, ParameterSetName='KernelDumpRecommended')] [Switch] $KernelDumpRecommended, # Initial page file size in MB (manual sizing) [Parameter(Mandatory=$true, ParameterSetName='ManualSize')] [ValidateRange(1024, 262144)] [uint32]$InitialSizeMB, # Maximum page file size in MB (manual sizing) [Parameter(Mandatory=$true, ParameterSetName='ManualSize')] [ValidateRange(1024, 262144)] [uint32]$MaximumSizeMB, # Scope: 'Node' (default) for local node only, 'Cluster' to apply to all cluster nodes [Parameter(Mandatory=$false)] [ValidateSet('Node','Cluster')] [string]$Scope = 'Node', [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")] [switch]$NoOutput ) begin { if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } # Backup current page file settings (skip in Cluster mode — each node backs up inline) if ($Scope -ne 'Cluster') { $null = Get-AzStackHciPageFileSettings } } process { # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active $script:SilentMode = $false if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # Determine target page file size if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { # Use same calculation as Set-AzStackHciMemoryDumpSettings / SetDedicatedDumpFileSize [uint32]$TargetSizeMB = SetDedicatedDumpFileSize if (-not $TargetSizeMB) { throw "Failed to calculate recommended page file size based on system memory." } $InitialSizeMB = $TargetSizeMB $MaximumSizeMB = $TargetSizeMB } if ($MaximumSizeMB -lt $InitialSizeMB) { throw "-MaximumSizeMB ($MaximumSizeMB) must be greater than or equal to -InitialSizeMB ($InitialSizeMB)." } $targetSizeGiB = [math]::Round($MaximumSizeMB / 1024, 1) # --------------------------------------------------------------- # Cluster scope: fan out to all cluster nodes via Invoke-Command # --------------------------------------------------------------- if ($Scope -eq 'Cluster') { if (-not (Test-CommandExists Get-Cluster)) { throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node." } try { [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop } catch { throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)" } Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose $sizeDescription = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { "Set kernel-dump-recommended page file (${targetSizeGiB} GiB) on $($Nodes.Count) cluster nodes" } else { "Set manual page file (Initial: ${InitialSizeMB} MB, Max: ${MaximumSizeMB} MB) on $($Nodes.Count) cluster nodes" } if (-not $PSCmdlet.ShouldProcess("Cluster '$ClusterName' ($($Nodes -join ', '))", $sizeDescription)) { return } $remoteArgs = @{ DriveLetter = $PageFileFileDriveLetter InitialSizeMB = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { 0 } else { $InitialSizeMB } MaximumSizeMB = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { 0 } else { $MaximumSizeMB } AutoSize = ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') } $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock { param($Params) $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Action = $null; PageFileSizeMB = 0; Error = $null } try { $DriveLetter = $Params.DriveLetter if ($DriveLetter.Length -eq 2) { $DriveLetter = "$DriveLetter\" } # Calculate size if auto-sizing # Tiers: <=128 GiB → 32 GiB, 129-767 GiB → 64 GiB, >=768 GiB → 128 GiB if ($Params.AutoSize) { $totalPhysicalMemory = (Get-CimInstance -ClassName Win32_PhysicalMemory -ErrorAction Stop | Measure-Object -Property Capacity -Sum).Sum [uint32]$InitSize = if ($totalPhysicalMemory -ge 768GB) { 131072 } elseif ($totalPhysicalMemory -le 128GB) { 32768 } else { 65536 } [uint32]$MaxSize = $InitSize } else { [uint32]$InitSize = $Params.InitialSizeMB [uint32]$MaxSize = $Params.MaximumSizeMB } # Get auto-managed state BEFORE the "already configured" check $PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).AutomaticManagedPagefile # Check if current settings already match the target $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if (-not $PageFileAutoManaged -and $pfConfig -and ($pfConfig | Measure-Object).Count -eq 1 -and $pfConfig.Name -eq "${DriveLetter}pagefile.sys" -and $pfConfig.InitialSize -eq $InitSize -and $pfConfig.MaximumSize -eq $MaxSize) { $result.Success = $true $result.Action = 'NoChangeRequired' $result.PageFileSizeMB = $MaxSize return $result } # Backup current settings (PageFileAutoManaged already queried above) $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' if (-not (Test-Path $BackupDir)) { New-Item $BackupDir -ItemType Directory -Force | Out-Null } $DateFormatted = Get-Date -f 'yyyyMMdd-HHmm' $BackupFile = "$BackupDir\PageFile_Settings_$DateFormatted.json" $pfConfig = $null if (-not $PageFileAutoManaged) { $pfSetting = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfSetting) { $pfConfig = @{ Name = $pfSetting.Name InitialSize = $pfSetting.InitialSize MaximumSize = $pfSetting.MaximumSize Caption = $pfSetting.Caption Description = $pfSetting.Description SettingID = $pfSetting.SettingID } } } $pfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue $backupObject = [PSCustomObject]@{ BackupDate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Node = $env:COMPUTERNAME PageFileAutoManaged = $PageFileAutoManaged PageFileConfig = $pfConfig PageFileUsage = @{ AllocatedBaseSize = if ($pfUsage) { $pfUsage.AllocatedBaseSize } else { $null } CurrentUsage = if ($pfUsage) { $pfUsage.CurrentUsage } else { $null } PeakUsage = if ($pfUsage) { $pfUsage.PeakUsage } else { $null } Name = if ($pfUsage) { $pfUsage.Name } else { $null } } } [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false))) # Check disk space: page file needs InitSize MB on the drive $Disk = Get-PSDrive -ErrorAction Stop | Where-Object { $_.Root -eq $DriveLetter } if (-not $Disk) { throw "Drive '$DriveLetter' not found on $env:COMPUTERNAME." } $currentPfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "$($DriveLetter.TrimEnd('\'))\*" } $currentPfSizeMB = if ($currentPfUsage) { $currentPfUsage.AllocatedBaseSize } else { 0 } # Net new space = target size - current page file size (if on same drive) $netNewMB = [math]::Max(0, $MaxSize - $currentPfSizeMB) $freeGiB = [math]::Round($Disk.Free / 1GB, 2) $neededGiB = [math]::Round($netNewMB / 1024 + 10, 1) # +10 GiB OS margin if ($freeGiB -lt $neededGiB) { throw "Insufficient disk space on $DriveLetter for ${MaxSize} MB page file. Free: ${freeGiB} GiB, Needed: ${neededGiB} GiB." } # Disable auto-management if enabled if ($PageFileAutoManaged) { $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } -ErrorAction Stop } # Remove existing page file config $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfConfig) { $pfConfig | Remove-CimInstance -ErrorAction Stop } # Create new page file $newPf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${DriveLetter}pagefile.sys" } -ErrorAction Stop $newPf | Set-CimInstance -Property @{ InitialSize = $InitSize; MaximumSize = $MaxSize } -ErrorAction Stop $result.PageFileSizeMB = $MaxSize $result.Success = $true } catch { $result.Error = $_.Exception.Message } $result } -ArgumentList (,$remoteArgs) -ErrorAction SilentlyContinue -ErrorVariable remoteErrors if ($remoteErrors) { foreach ($err in $remoteErrors) { Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose } } $successCount = 0; $failCount = 0; $noChangeCount = 0 foreach ($nodeResult in $remoteResults) { if ($nodeResult.Success -and $nodeResult.Action -eq 'NoChangeRequired') { Write-Verbose " $($nodeResult.Node): Already configured ($($nodeResult.PageFileSizeMB) MB) — no changes needed" -Verbose $noChangeCount++; $successCount++ } elseif ($nodeResult.Success) { Write-Verbose " $($nodeResult.Node): SUCCESS ($($nodeResult.PageFileSizeMB) MB)" -Verbose; $successCount++ } else { Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)" -Verbose; $failCount++ } } Write-Verbose "Cluster operation complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose if ($failCount -gt 0) { Write-Warning "$failCount node(s) failed. Review the output above for details." } return $remoteResults } # --------------------------------------------------------------- # Node scope: set manual page file on local node # --------------------------------------------------------------- # Normalize drive letter if ($PageFileFileDriveLetter.Length -eq 2) { $PageFileFileDriveLetter = "$PageFileFileDriveLetter\" } # Check if current settings already match the target $dumpDrive = $PageFileFileDriveLetter.TrimEnd('\') [bool]$PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).AutomaticManagedPagefile $currentPfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue $currentPfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "${dumpDrive}\*" } $currentPfSizeMB = if ($currentPfUsage) { $currentPfUsage.AllocatedBaseSize } else { 0 } if (-not $PageFileAutoManaged -and $currentPfConfig -and ($currentPfConfig | Measure-Object).Count -eq 1 -and $currentPfConfig.Name -eq "${PageFileFileDriveLetter}pagefile.sys" -and $currentPfConfig.InitialSize -eq $InitialSizeMB -and $currentPfConfig.MaximumSize -eq $MaximumSizeMB) { Write-Verbose "Page file is already configured as requested: ${PageFileFileDriveLetter}pagefile.sys (Initial: ${InitialSizeMB} MB, Max: ${MaximumSizeMB} MB). No changes needed." -Verbose return [PSCustomObject]@{ Node = $env:COMPUTERNAME Success = $true Action = 'NoChangeRequired' InitialSizeMB = $InitialSizeMB MaximumSizeMB = $MaximumSizeMB PreviousAutoManaged = $false Error = $null } } # Check disk space $Disk = Get-PSDrive -ErrorAction Stop | Where-Object { $_.Root -eq $PageFileFileDriveLetter } if (-not $Disk) { throw "Drive '$PageFileFileDriveLetter' not found." } $netNewMB = [math]::Max(0, $MaximumSizeMB - $currentPfSizeMB) $freeGiB = [math]::Round($Disk.Free / 1GB, 2) $neededGiB = [math]::Round($netNewMB / 1024 + 10, 1) if ($freeGiB -lt $neededGiB) { throw "Insufficient disk space on '$PageFileFileDriveLetter' for ${MaximumSizeMB} MB page file. Free: ${freeGiB} GiB, Needed: ~${neededGiB} GiB (${netNewMB} MB net new + 10 GiB OS margin). Current page file on this drive: ${currentPfSizeMB} MB." } # ShouldProcess gate $actionDescription = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { "Set kernel-dump-recommended manual page file (${InitialSizeMB} MB / ${targetSizeGiB} GiB) on ${PageFileFileDriveLetter}" } else { "Set manual page file (Initial: ${InitialSizeMB} MB, Max: ${MaximumSizeMB} MB) on ${PageFileFileDriveLetter}" } if (-not $PSCmdlet.ShouldProcess("Page file on $PageFileFileDriveLetter", $actionDescription)) { return } # Disable auto-management if enabled (already queried above) if ($PageFileAutoManaged) { Write-Verbose "Disabling automatic page file management..." -Verbose $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } -ErrorAction Stop Write-Verbose "Automatic page file management disabled." -Verbose } # Remove existing page file config $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfConfig) { if (($pfConfig | Measure-Object).Count -eq 1) { $pfConfig | Remove-CimInstance -ErrorAction Stop Write-Verbose "Existing page file settings removed." -Verbose } else { throw "Unexpected: more than one page file configured (found $(($pfConfig | Measure-Object).Count)). Remove extra page files manually first." } } # Create new page file with specified size try { $PageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${PageFileFileDriveLetter}pagefile.sys" } -ErrorAction Stop $PageFile | Set-CimInstance -Property @{ InitialSize = $InitialSizeMB; MaximumSize = $MaximumSizeMB } -ErrorAction Stop } catch { throw "Failed to create page file with InitialSize=${InitialSizeMB} MB, MaximumSize=${MaximumSizeMB} MB. Error: $($_.Exception.Message)" } if (-not $PageFile) { throw "Failed to configure page file." } Write-Verbose "Page file configured: ${PageFileFileDriveLetter}pagefile.sys (InitialSize: ${InitialSizeMB} MB, MaximumSize: ${MaximumSizeMB} MB / ${targetSizeGiB} GiB)" -Verbose Write-Verbose "Automatic page file management has been disabled." -Verbose Write-Verbose "A system restart is required for page file changes to take effect." -Verbose [PSCustomObject]@{ Node = $env:COMPUTERNAME Success = $true Action = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { 'SetPageFileKernelDumpRecommended' } else { 'SetPageFileManual' } InitialSizeMB = $InitialSizeMB MaximumSizeMB = $MaximumSizeMB PreviousAutoManaged = $PageFileAutoManaged Error = $null } } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Set-AzStackHciPageFileSettings function" } } # End of Set-AzStackHciPageFileSettings # /////////////////////////////////////////////////////////////////// # Set-AzStackHciPageFileSettingsMinimal Function # Used to set a static 4GB page file on the system, target drive is # the only parameter. # /////////////////////////////////////////////////////////////////// Function Set-AzStackHciPageFileSettingsMinimal { <# .SYNOPSIS Sets page file settings to minimum 4GB fixed size .DESCRIPTION Queries the system for current page file settings and sets a fixed 4GB page file. Use -Scope Cluster to apply settings to all nodes in the failover cluster via Invoke-Command. The scriptblock is self-contained and does not require the module on remote nodes. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([PSCustomObject[]])] param ( # Path to drive letter for PageFile [Parameter(Mandatory=$true,Position=1)] [ValidateScript({Test-Path $_})] [String] [ValidatePattern('^[C-Zc-z]:\\?$')]$PageFileFileDriveLetter, # Drive letter for the page file, must be a valid drive letter (C: through Z:) # Scope: 'Node' (default) for local node only, 'Cluster' to apply to all cluster nodes [Parameter(Mandatory=$false)] [ValidateSet('Node','Cluster')] [string]$Scope = 'Node', [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")] [switch]$NoOutput ) begin { # Requires administrator permissions to set page file settings if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } # Call function to get current Page File settings and save to backup log file (skip in Cluster mode — each node backs up inline) if ($Scope -ne 'Cluster') { $null = Get-AzStackHciPageFileSettings } } process { # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active $script:SilentMode = $false # Handle -NoOutput: suppress all console output if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # --------------------------------------------------------------- # Cluster scope: fan out to all cluster nodes via Invoke-Command # --------------------------------------------------------------- if ($Scope -eq 'Cluster') { if (-not (Test-CommandExists Get-Cluster)) { throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node." } try { [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop } catch { throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)" } Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose if (-not $PSCmdlet.ShouldProcess("Cluster '$ClusterName' ($($Nodes -join ', '))", "Set fixed 4GB page file on $($Nodes.Count) cluster nodes")) { return } $remoteDriveLetter = $PageFileFileDriveLetter # Self-contained scriptblock — does NOT depend on the module being installed on remote nodes $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock { param($DriveLetter) $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Error = $null } try { # Normalize drive letter if ($DriveLetter.Length -eq 2) { $DriveLetter = "$DriveLetter\" } # ── Inline backup of current page file settings ── $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' if (-not (Test-Path $BackupDir)) { New-Item $BackupDir -ItemType Directory -Force | Out-Null } $DateFormatted = Get-Date -f 'yyyyMMdd-HHmm' $BackupFile = "$BackupDir\PageFile_Settings_$DateFormatted.json" $PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).AutomaticManagedPagefile $pfConfigBackup = $null if (-not $PageFileAutoManaged) { $pfSetting = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfSetting) { $pfConfigBackup = @{ Name = $pfSetting.Name InitialSize = $pfSetting.InitialSize MaximumSize = $pfSetting.MaximumSize Caption = $pfSetting.Caption Description = $pfSetting.Description SettingID = $pfSetting.SettingID } } } $pfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue $backupObject = [PSCustomObject]@{ BackupDate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Node = $env:COMPUTERNAME PageFileAutoManaged = $PageFileAutoManaged PageFileConfig = $pfConfigBackup PageFileUsage = @{ AllocatedBaseSize = if ($pfUsage) { $pfUsage.AllocatedBaseSize } else { $null } CurrentUsage = if ($pfUsage) { $pfUsage.CurrentUsage } else { $null } PeakUsage = if ($pfUsage) { $pfUsage.PeakUsage } else { $null } Name = if ($pfUsage) { $pfUsage.Name } else { $null } } } [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false))) # ── Set 4GB fixed page file ── if (-not $PageFileAutoManaged) { # Manual: remove existing page file config, create 4GB fixed $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfConfig) { if (($pfConfig | Measure-Object).Count -eq 1) { $pfConfig | Remove-CimInstance -ErrorAction Stop } else { throw "Unexpected: more than one page file configured (found $(($pfConfig | Measure-Object).Count))" } } $newPf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${DriveLetter}pagefile.sys" } -ErrorAction Stop $newPf | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } -ErrorAction Stop } else { # Auto-managed: disable auto management, then create 4GB fixed $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } -ErrorAction Stop $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfConfig) { if (($pfConfig | Measure-Object).Count -eq 1) { $pfConfig | Remove-CimInstance -ErrorAction Stop } else { throw "Unexpected: more than one page file configured (found $(($pfConfig | Measure-Object).Count))" } } $newPf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${DriveLetter}pagefile.sys" } -ErrorAction Stop $newPf | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } -ErrorAction Stop } if (-not $newPf) { throw 'Failed to configure page file' } $result.Success = $true } catch { $result.Error = $_.Exception.Message } $result } -ArgumentList $remoteDriveLetter -ErrorAction SilentlyContinue -ErrorVariable remoteErrors # Report per-node results if ($remoteErrors) { foreach ($err in $remoteErrors) { Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose } } $successCount = 0 $failCount = 0 foreach ($nodeResult in $remoteResults) { if ($nodeResult.Success) { Write-Verbose " $($nodeResult.Node): SUCCESS" -Verbose $successCount++ } else { Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)" -Verbose $failCount++ } } Write-Verbose "Cluster operation complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose if ($failCount -gt 0) { Write-Warning "$failCount node(s) failed. Review the output above for details." } return $remoteResults } # if the drive letter is only 2 characters (C:), add a backslash to the end (C:\) if($PageFileFileDriveLetter.Length -eq 2){ $PageFileFileDriveLetter = $($PageFileFileDriveLetter + "\") } # ShouldProcess gate: prompt user before modifying page file settings if (-not $PSCmdlet.ShouldProcess("Page file on $PageFileFileDriveLetter", "Set fixed 4GB page file")) { return } try { # Get current page file settings [bool]$script:PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile } catch { Write-Verbose "Failed to get current page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Failed to get current page file settings. Error: $($_.Exception.Message)" } # If page file is NOT automatically managed if($script:PageFileAutoManaged -eq $False){ # Page File manual settings Write-HostAzS "Automatic management of page file already disabled" Write-HostAzS "Configuring a fixed page file of 4GB size using file: $($PageFileFileDriveLetter)pagefile.sys`n" try { $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting } catch { Write-Verbose "Failed to get existing page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to get existing page file settings. Error: $($_.Exception.Message)" } # Remove existing manual page file settings if($PageFileConfiguration){ if(($PageFileConfiguration | Measure-Object).Count -eq 1){ try { # Delete existing page file settings $PageFileConfiguration | Remove-CimInstance } catch { Write-Verbose "Failed to delete existing page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to delete existing page file settings. Error: $($_.Exception.Message)" } Write-Verbose "Existing page file settings removed." -Verbose } else { Write-Verbose "Unexpected configuration, more than one page file is currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." -Verbose Throw "Error: Unexpected configuration, more than one page file currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." } } else { Write-Verbose "Existing page file settings not found." -Verbose } # Set a New page file, and set the size to 4GB fixed try { # Use New-CimInstance to create a new page file setting, due to "Set-WmiInstance : Generic failure" error, if no existing page file settings are configured (as deleted). $PageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name= "$($PageFileFileDriveLetter)pagefile.sys" } $PageFile | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } } catch { Write-Verbose "Failed to set minimum page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Failed to set minimum page file settings. Error: $($_.Exception.Message)" } if($PageFile){ Write-Verbose "Page File settings updated successfully to a fixed size of 4GB" -Verbose Write-Verbose "Page File: = '$($pagefile.Description)'" -Verbose } else { Write-Verbose "Failed to configure minimum page file settings." -Verbose Throw "Error: Failed to configure minimum page file settings." } [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'SetMinimumPageFile'; PreviousAutoManaged = $false; Error = $null } } elseif($script:PageFileAutoManaged -eq $True){ # If the page file is currently automatically managed Write-HostAzS "Disabling automatic management of page file" # Remove System Managed: try { $ComputerSystem = Get-CimInstance -ClassName Win32_ComputerSystem } catch { Write-Verbose "Failed to get existing automatic management settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to get existing automatic management settings. Error: $($_.Exception.Message)" } try { $ComputerSystem | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } } catch { Write-Verbose "Failed to disable automatic management of page file. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to disable automatic management of page file. Error: $($_.Exception.Message)" } Write-Verbose "Automatic management of page file disabled" -Verbose try { $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting } catch { Write-Verbose "Failed to get existing page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to get existing page file settings. Error: $($_.Exception.Message)" } Write-HostAzS "Configuring a fixed page file of 4GB size using file: $($PageFileFileDriveLetter)pagefile.sys`n" # Remove existing page file setting and set a new page file, and set the size to 4GB fixed if($PageFileConfiguration){ if(($PageFileConfiguration | Measure-Object).Count -eq 1){ try { # Delete existing page file settings $PageFileConfiguration | Remove-CimInstance } catch { Write-Verbose "Failed to delete existing page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to delete existing page file settings. Error: $($_.Exception.Message)" } Write-Verbose "Existing page file settings removed." -Verbose } else { Write-Verbose "Unexpected configuration, more than one page file is currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." -Verbose Throw "Error: Unexpected configuration, more than one page file currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." } # Set a New page file, and set the size to 4GB fixed try { # Use New-CimInstance to create a new page file setting, due to "Set-WmiInstance : Generic failure" error, if no existing page file settings are configured (as deleted). $PageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name= "$($PageFileFileDriveLetter)pagefile.sys" } $PageFile | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } } catch { Write-Verbose "Failed to set new page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Failed to set new page file settings. Error: $($_.Exception.Message)" } if($PageFile){ Write-Verbose "Page File settings updated successfully to a fixed size of 4GB" -Verbose Write-Verbose "Page File: = '$($pagefile.Description)'" -Verbose } else { Write-Verbose "Failed to configure minimum page file settings." -Verbose Throw "Error: Failed to configure minimum page file settings." } Write-Verbose "Page File settings updated to minimum recommended configuration, (static size of 4GB)." -Verbose } # End of If PageFileConfiguration Write-Verbose "A system restart is required for Page File changes to take effect.`n`n" -Verbose [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'SetMinimumPageFile'; PreviousAutoManaged = $true; Error = $null } } else { Write-Error "Current Settings: Page File automatic management is Unknown (not enabled or disabled)." -Verbose Throw "Error: Current Settings: Page File automatic management is Unknown (not enabled or disabled)." } # End of If PageFileAutoManaged } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Set-AzStackHciPageFileSettingsMinimal function" } } # End of Set-AzStackHciPageFileSettingsMinimal # /////////////////////////////////////////////////////////////////// # Restore-AzStackHciPageFileSettings function # Used to restore or roll back page file settings # /////////////////////////////////////////////////////////////////// Function Restore-AzStackHciPageFileSettings { <# .SYNOPSIS Restores page file settings from a backup file .DESCRIPTION Restores the page file settings from a backup file, using the 'PageFile_Settings_YYYYMMDD.json' file. Requires administrator permissions to restore page file settings. Use -Scope Cluster to restore settings on all nodes in the failover cluster via Invoke-Command. In Cluster mode, each node auto-discovers its most recent backup file if no path is specified. The scriptblock is self-contained and does not require the module on remote nodes. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([PSCustomObject[]])] param ( # File path to backup file (required for Node scope; optional for Cluster scope where auto-discovery is used) [Parameter(Mandatory=$false,Position=0)] [ValidateScript({Test-Path $_})] [String]$PageFileSettingsFilePath, # File used to restore settings from, must be a valid path # Scope: 'Node' (default) for local node only, 'Cluster' to restore on all cluster nodes [Parameter(Mandatory=$false)] [ValidateSet('Node','Cluster')] [string]$Scope = 'Node', [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")] [switch]$NoOutput ) begin { # Requires administrator permissions to set page file settings if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } } process { # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active $script:SilentMode = $false # Handle -NoOutput: suppress all console output if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # --------------------------------------------------------------- # Cluster scope: fan out to all cluster nodes via Invoke-Command # --------------------------------------------------------------- if ($Scope -eq 'Cluster') { if (-not (Test-CommandExists Get-Cluster)) { throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node." } try { [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop } catch { throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)" } Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose if (-not $PSCmdlet.ShouldProcess("Cluster '$ClusterName' ($($Nodes -join ', '))", "Restore page file settings from backup on $($Nodes.Count) cluster nodes")) { return } # Pass the file path if provided; otherwise each node auto-discovers its latest backup $remoteFilePath = if ($PSBoundParameters.ContainsKey('PageFileSettingsFilePath')) { $PageFileSettingsFilePath } else { $null } # Self-contained scriptblock — does NOT depend on the module being installed on remote nodes $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock { param($FilePath) $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Action = $null; BackupFile = $null; Error = $null } try { $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' # Auto-discover the most recent backup file if no path was provided if ([string]::IsNullOrEmpty($FilePath)) { $LatestFile = Get-ChildItem "$BackupDir\PageFile_Settings_*.json" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if (-not $LatestFile) { throw "WARNING: No PageFile backup files found in '$BackupDir' on $env:COMPUTERNAME. Run Get-AzStackHciPageFileSettings first." } $FilePath = $LatestFile.FullName } $result.BackupFile = $FilePath # Validate file path if ($FilePath -notlike 'C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_*') { throw "Backup file path must match 'C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_*'. Got: '$FilePath'" } if (-not (Test-Path $FilePath)) { throw "Backup file not found: '$FilePath'" } $backup = Get-Content $FilePath -Raw -ErrorAction Stop | ConvertFrom-Json $wasAutoManaged = $backup.PageFileAutoManaged $currentPageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile if ($wasAutoManaged) { # Backup had auto-managed: restore to auto-managed $nameValue = $backup.PageFileUsage.Name if (-not $nameValue) { throw 'Could not find page file Name in backup file' } if ($currentPageFileAutoManaged) { throw 'Page File is already automatically managed, no action required.' } # Remove current manual page file $currentPf = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($currentPf -and ($currentPf | Measure-Object).Count -eq 1) { $currentPf | Remove-CimInstance -ErrorAction Stop } elseif ($currentPf -and ($currentPf | Measure-Object).Count -gt 1) { throw "Unexpected: $(($currentPf | Measure-Object).Count) page files configured" } # Create with size 0 (system-managed) $pf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $nameValue } $pf | Set-CimInstance -Property @{ InitialSize = 0; MaximumSize = 0 } # Enable automatic management $cs = Get-CimInstance -ClassName Win32_ComputerSystem $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $true } } else { # Backup had manual config: restore with backup's settings $restoreName = $backup.PageFileConfig.Name $restoreInitial = [int]$backup.PageFileConfig.InitialSize $restoreMax = [int]$backup.PageFileConfig.MaximumSize if (-not $restoreName -or $null -eq $restoreInitial -or $null -eq $restoreMax) { throw 'Could not parse page file Name, InitialSize, or MaximumSize from backup file' } # Remove current config $currentPf = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($currentPf -and ($currentPf | Measure-Object).Count -eq 1) { $currentPf | Remove-CimInstance -ErrorAction Stop } elseif ($currentPf -and ($currentPf | Measure-Object).Count -gt 1) { throw "Unexpected: $(($currentPf | Measure-Object).Count) page files configured" } # Restore with backup settings $pf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $restoreName } $pf | Set-CimInstance -Property @{ InitialSize = $restoreInitial; MaximumSize = $restoreMax } } $result.Action = if ($wasAutoManaged) { 'RestoreAutoManaged' } else { 'RestoreManual' } $result.Success = $true } catch { $result.Error = $_.Exception.Message } $result } -ArgumentList $remoteFilePath -ErrorAction SilentlyContinue -ErrorVariable remoteErrors # Report per-node results if ($remoteErrors) { foreach ($err in $remoteErrors) { Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose } } $successCount = 0 $failCount = 0 foreach ($nodeResult in $remoteResults) { $backupInfo = if ($nodeResult.BackupFile) { " (from $($nodeResult.BackupFile))" } else { '' } if ($nodeResult.Success) { Write-Verbose " $($nodeResult.Node): SUCCESS$backupInfo" -Verbose $successCount++ } else { Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)$backupInfo" -Verbose $failCount++ } } Write-Verbose "Cluster restore complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose if ($failCount -gt 0) { Write-Warning "$failCount node(s) failed. Review the output above for details." } return $remoteResults } # --------------------------------------------------------------- # Node scope: auto-discover latest backup if no path provided # --------------------------------------------------------------- if ([string]::IsNullOrEmpty($PageFileSettingsFilePath)) { $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' $LatestFile = Get-ChildItem "$BackupDir\PageFile_Settings_*.json" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if (-not $LatestFile) { Write-Warning "No PageFile backup files found in '$BackupDir' on $env:COMPUTERNAME." throw "No PageFile backup files found in '$BackupDir'. Run Get-AzStackHciPageFileSettings first to create a backup." } $PageFileSettingsFilePath = $LatestFile.FullName Write-Verbose "Auto-discovered latest backup file: $PageFileSettingsFilePath (Last modified: $($LatestFile.LastWriteTime))" -Verbose } # Validate backup file path if ($PageFileSettingsFilePath -notlike "C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_*") { throw "Error: Page File settings backup file path must start with 'C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_'" } # Read and parse the JSON backup file Write-Verbose "Reading Page File settings from backup file: $PageFileSettingsFilePath" -Verbose $backup = Get-Content $PageFileSettingsFilePath -Raw -ErrorAction Stop | ConvertFrom-Json try { # Get current page file settings [bool]$script:PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile } catch { throw "Failed to get current page file settings. Error: $($_.Exception.Message)" } # ShouldProcess confirmation before restoring if (-not $PSCmdlet.ShouldProcess("Page file settings on $env:COMPUTERNAME", "Restore page file settings from '$PageFileSettingsFilePath' (saved on $($backup.BackupDate))")) { return } [bool]$RestorePageFileAutoManaged = $backup.PageFileAutoManaged if ($RestorePageFileAutoManaged) { # Backup had auto-managed: restore to auto-managed $nameValue = $backup.PageFileUsage.Name if (-not $nameValue) { throw 'Could not find page file Name in backup file' } if ($script:PageFileAutoManaged) { throw "Restore Page File settings: Page File is already automatically managed, no action required." } try { # Remove existing manual page file $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting if (($script:PageFileConfiguration | Measure-Object).Count -eq 1) { $script:PageFileConfiguration | Remove-CimInstance -ErrorAction Stop Write-Verbose "Existing manual page file settings removed." -Verbose } elseif (($script:PageFileConfiguration | Measure-Object).Count -gt 1) { throw "Unexpected: $(($script:PageFileConfiguration | Measure-Object).Count) page files configured" } # Create system-managed page file $RestorePageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $nameValue } -ErrorAction Stop $RestorePageFile | Set-CimInstance -Property @{ InitialSize = 0; MaximumSize = 0 } -ErrorAction Stop # Enable automatic management $ComputerSystem = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $ComputerSystem | Set-CimInstance -Property @{ AutomaticManagedPagefile = $true } -ErrorAction Stop } catch { throw "Failed to restore auto-managed page file settings. Error: $($_.Exception.Message)" } Write-Verbose "Page File settings reverted to automatically managed." -Verbose Write-Verbose "A system restart is required for the Page File changes to take effect." -Verbose return [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'RestoreAutoManaged'; BackupFile = $PageFileSettingsFilePath; Error = $null } } else { # Backup had manual config: restore with backup's settings $restoreName = $backup.PageFileConfig.Name $restoreInitial = [int]$backup.PageFileConfig.InitialSize $restoreMax = [int]$backup.PageFileConfig.MaximumSize if (-not $restoreName -or $null -eq $restoreInitial -or $null -eq $restoreMax) { throw 'Could not parse page file Name, InitialSize, or MaximumSize from backup file' } try { # Remove existing page file $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting if (($script:PageFileConfiguration | Measure-Object).Count -eq 1) { $script:PageFileConfiguration | Remove-CimInstance -ErrorAction Stop Write-Verbose "Existing page file settings removed." -Verbose } elseif (($script:PageFileConfiguration | Measure-Object).Count -gt 1) { throw "Unexpected: $(($script:PageFileConfiguration | Measure-Object).Count) page files configured" } # Restore with backup settings $RestorePageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $restoreName } -ErrorAction Stop $RestorePageFile | Set-CimInstance -Property @{ InitialSize = $restoreInitial; MaximumSize = $restoreMax } -ErrorAction Stop } catch { throw "Failed to restore manual page file settings. Error: $($_.Exception.Message)" } Write-Verbose "Page File configured with InitialSize = $restoreInitial and MaximumSize = $restoreMax" -Verbose Write-Verbose "A system restart is required for the Page File changes to take effect." -Verbose return [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'RestoreManual'; BackupFile = $PageFileSettingsFilePath; Error = $null } } } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Restore-AzStackHciPageFileSettings function" } } # End of Restore-AzStackHciPageFileSettings function # SIG # Begin signature block # MIInUAYJKoZIhvcNAQcCoIInQTCCJz0CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB9tbyCRMkXzT1y # jVAKvdb+ZVMgucYJzBWrueLXsnxdW6CCDMkwggYEMIID7KADAgECAhMzAAACHPrN # xZvoL37EAAAAAAIcMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQxWhcNMjcwNDE1MTg1 # OTQxWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDVsZfgOKmM31HPfoWOoNEiw0SlCiIxUMC0I9NMWbucKOw/e9lP # oAoehQVu6SG65V4EPzrYsnBnFPNoi4/HoOdjhz1qkrEt4I6tEcxXU6oOeY9zGveC # /3iBeuhLYxM3M/PkcUoebF+Nednm8OkdSPoDu8imViHPQq/8CQUu0WRR4rE+dMRf # rpVqfmNi2qWCX94T4MsepijGVkwE//tJg0ryAiYdHT34LSnlG/RSBZmQRGWZ5g8j # qnKjRParSqMft1gvjuUTVgtWNZfgcLFSK5Wa0myrq8OPcgTGGsRgun+tnSS+IxDT # xVsAPH1OzvPjwomguByhUe/OcvUN0D5Wmp7xAgMBAAGjggGqMIIBpjAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFNoH7a2YDjOSwpkp6DHcmUS7J+0yMFQGA1UdEQRNMEukSTBHMS0wKwYDVQQL # EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxFjAUBgNVBAUT # DTIzMDAxMis1MDc1NjkwHwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEw # YAYDVR0fBFkwVzBVoFOgUYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w # cy9jcmwvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # bDBtBggrBgEFBQcBAQRhMF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9z # b2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmcl # MjBQQ0ElMjAyMDI0LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IC # AQAUnEqhaRXe0T3hIJjvdQErEkrA/7bByjn6t5IArODkkRjzkYwtKMc2yYj2quaN # rLutWw2YZcngKPy1b71YyDJQTy4NDRwaSh9Tw5thrk3NmcPrAHia5vtcBJ1CgtKK # 7mQbIcQ22d/N3813ayCDDFewu1+jsZmX+r/aTEqaOM4TVxVtRSkuCy8nAXKuChOK # Li/zA4XuH8iEYqIsj2YoNaeSxVmeGiERXpKdo3dDmYi0kO5w2D8VS4c3+9h6gElY # BaAAg/dYErBg27qT3vv0zRDJhJufvCNylA8S7/+8H5E/PV5cng6na9VV/w9OV3qu # uND6zdGa2EX38Glp50F9AIQk3p2xXmcvorDeM4XJ7UlWYBi6g80J1SSOQnInCYFE # msfUNn3+1AaTJKSJL83quKArTac2pKhu0Yzzzrzo6HrsRiQKzpnRBb1/dMa6P3hz # 75XbMRBctNsFhZC07WCmjExdLg2eHW5uV0TY8D5+6wozJf7vF3+WHkYPO85Z+BC6 # U4FkNbYNycZ9cE4j1tXRdyDCfml6c0HWPHjNVDObrv9lKt3qUqFpX38VCqVCyNOO # 1UcXfQiVjJw32U2WUKZjt/neJKHEBsm9kFsLuWzkQ53+qcaSaytmsCnk2gOglrlD # 5d3kKyvvAw+rzm0lT8K38P6PLxfZQHhu4W8dV7Av8N2ZmDCCBr0wggSloAMCAQIC # EzMAAAA5O7Y3Gb8GHWcAAAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBS # b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoX # DTM2MDMyMjIyMTMwNFowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQ # Q0EgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeq # lRYHNa265v4IY9fH8TKhemHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo # 0dtS/EW6I/yEL/bLSY8hKpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATv # QVL4tcf03aTycsz8QeCdM0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a # 1uv1zerOYMnsneRRwCbpyW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1 # FyQfK0fVkaya8SmVHQ/tOf23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfO # GSWHIIV4YrTJTT6PNty5REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7 # ttOu1bVnXfHaqPYl2rPs20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJ # uz2MXMCt7iw7lFPG9LXKGjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxS # CwyoGIq0PhaA7Y+VPct5pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOm # VQop36wUVUYklUy++vDWeEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3 # SkE/xIkgpfl22MM1itkZ35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8E # BAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPX # LQaUEggxMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMB # Af8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBP # oE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv # TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAw # TgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv # TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOC # AgEAFJQfOChP7onn6fLIMKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D # 5W4wMwYeLystcEqfkjz4NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBY # nbu0+THSuVHTe0VTTPVhily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSI # vgn0JksVBVMYVI5QFu/qhnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6 # aR9y34aiM1qmxaxBi6OUnyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4w # PKC5OmHm1DQIt/MNokbbH3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7 # RTX8AdBPo0I6OEojf39zuFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK # /fg8B2qjW88MT/WF5V5uvZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSK # YBv0VisCzfxgeU+dquXW9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkw # YTu/9dLeH2pDqeJZAABVDWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVT # Ql0v4q8J/AUmQN5W4n101cY2L4A7GTQG1h32HHAvfQESWP0xghndMIIZ2QIBATBu # MFcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIc # +s3Fm+gvfsQAAAAAAhwwDQYJYIZIAWUDBAIBBQCggZAwGQYJKoZIhvcNAQkDMQwG # CisGAQQBgjcCAQQwLwYJKoZIhvcNAQkEMSIEIEQ9ghRWoS1bqbKpK7STbJcqqAIp # tC9CGXc7YxHBfR/xMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBv # AGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAE # ggEAYqYKXq5R3zbA4rFGh7ltWWSBRNWkvS/Ciku26UPweEA8f8sezYe+ilyHnvoo # 9inDa0YvpnJS1cjx2t6+17w1gojd0OpdIuN4Xp+wFUfDsT+k8aszPddKCAv7NtRX # LjoSaBzJ4D/LqRGoulNqkEb3fo0SIYqpng+eDs7AIKG3zIYHomQ9WEA0sdbpeV6B # XJrw+2acHUDpaHSyhCi1+Y48FYH3AlKKn+RpAqVzEmvxWmg7QIh4TkBnR8e6x4ba # b/Of32m0JAXwqXUiup11FCWE3FFPN7CYmmjSrSmGH4WoO0CprdSMr3coU5ambg6w # 2/lYsMa3d9w3C8Tvuem2QgsJv6GCF60wghepBgorBgEEAYI3AwMBMYIXmTCCF5UG # CSqGSIb3DQEHAqCCF4YwgheCAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsqhkiG # 9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQC # AQUABCClcz9KRl6LPwUgivNIJpvuqshJH4cp4BWmHF9qtVDEpwIGamQr1NKRGBMy # MDI2MDcyOTEzMTcyOS44MTJaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFu # ZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo2 # NTFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaCCEfswggcoMIIFEKADAgECAhMzAAACFRgD04EHJnxTAAEAAAIVMA0GCSqG # SIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDgx # NDE4NDgyMFoXDTI2MTExMzE4NDgyMFowgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjY1MUEtMDVF # MC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw3HV3hVxL0lEYPV03XeNKZ51 # 7VIbgexhlDPdpXwDS0BYtxPwi4XYpZR1ld0u6cr2Xjuugdg50DUx5WHL0QhY2d9v # kJSk02rE/75hcKt91m2Ih287QRxRMmFu3BF6466k8qp5uXtfe6uciq49YaS8p+dz # v3uTarD4hQ8UT7La95pOJiRqxxd0qOGLECvHLEXPXioNSx9pyhzhm6lt7ezLxJeF # VYtxShkavPoZN0dOCiYeh4KgoKoyagzMuSiLCiMUW4Ue4Qsm658FJNGTNh7V5qXY # VA6k5xjw5WeWdKOz0i9A5jBcbY9fVOo/cA8i1bytzcDTxb3nctcly8/OYeNstkab # /Isq3Cxe1vq96fIHE1+ZGmJjka1sodwqPycVp/2tb+BjulPL5D6rgUXTPF84U82R # LKHV57bB8fHRpgnjcWBQuXPgVeSXpERWimt0NF2lCOLzqgrvS/vYqde5Ln9YlKKh # AZ/xDE0TLIIr6+I/2JTtXP34nfjTENVqMBISWcakIxAwGb3RB5yHCxynIFNVLcfK # AsEdC5U2em0fAvmVv0sonqnv17cuaYi2eCLWhoK1Ic85Dw7s/lhcXrBpY4n/Rl5l # 3wHzs4vOIhu87DIy5QUaEupEsyY0NWqgI4BWl6v1wgse+l8DWFeUXofhUuCgVTuT # HN3K8idoMbn8Q3edUIECAwEAAaOCAUkwggFFMB0GA1UdDgQWBBSJIXfxcqAwFqGj # 9jdwQtdSqadj1zAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNV # HR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2Ny # bC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYI # KwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAy # MDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMI # MA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAd42HtV+kGbvxzLBT # C5O7vkCIBPy/BwpjCzeL53hAiEOebp+VdNnwm9GVCfYq3KMfrj4UvKQTUAaS5Zkw # e1gvZ3ljSSnCOyS5OwNu9dpg3ww+QW2eOcSLkyVAWFrLn6Iig3TC/zWMvVhqXtdF # hG2KJ1lSbN222csY3E3/BrGluAlvET9gmxVyyxNy59/7JF5zIGcJibydxs94JL1B # tPgXJOfZzQ+/3iTc6eDtmaWT6DKdnJocp8wkXKWPIsBEfkD6k1Qitwvt0mHrORah # 75SjecOKt4oWayVLkPTho12e0ongEg1cje5fxSZGthrMrWKvI4R7HEC7k8maH9eP # A3ViH0CVSSOefaPTGMzIhHCo5p3jG5SMcyO3eA9uEaYQJITJlLG3BwwGmypY7C/8 # /nj1SOhgx1HgJ0ywOJL9xfP4AOcWmCfbsqgGbCaC7WH5sINdzfMar8V7YNFqkbCG # UKhc8GpIyE+MKnyVn33jsuaGAlNRg7dVRUSoYLJxvUsw9GOwyBpBwbE9sqOLm+Hs # O00oF23PMio7WFXcFTZAjp3ujihBAfLrXICgGOHPdkZ042u1LZqOcnlr3XzvgMe+ # mPPyasW8f0rtzJj3V5E/EKiyQlPxj9Mfq2x9himnlXWGZCVPeEBROrNbDYBfazTy # LNCOTsRtksOSV3FBtPnpQtLN754wggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZ # AAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMyMjVa # MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT # HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEF # AAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1 # V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9 # alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmv # Haus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN7928 # jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3t # pK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74kpEe # HT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2K26o # ElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5TI4C # vEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZki1ug # poMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9QBXps # xREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0C # AwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYE # FCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtT # NRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNo # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5o # dG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBD # AEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZW # y4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5t # aWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAt # MDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0y # My5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pc # FLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpT # Td2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0j # VOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3 # +SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmR # sqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSw # ethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPXfx5b # RAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVXVAmx # aQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGConsX # HRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU5nR0 # W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEGahC0 # HVUzWLOhcGbyoYIDVjCCAj4CAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFu # ZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo2 # NTFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZaIjCgEBMAcGBSsOAwIaAxUAj6eTejbuYE1Ifjbfrt6tXevCUSCggYMwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsFAAIF # AO4T7/0wIhgPMjAyNjA3MjkwMzIwMjlaGA8yMDI2MDczMDAzMjAyOVowdDA6Bgor # BgEEAYRZCgQBMSwwKjAKAgUA7hPv/QIBADAHAgEAAgIaSTAHAgEAAgIT+jAKAgUA # 7hVBfQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID # B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQBflTR+tuVJhPGQgc7T # qQHT52+Of7xuVVjG0S2KbWu6HsWah5g23lmpyyeQlHSQ3B4X4zrg0JqhksEZPwaq # iKHShGyfinqzrNxauCDEwwt3mD7XshyJK5R/vd1J/k4h2pMH35h1e9L0Zhi3QcT3 # bPl3xwPSzr3Cnw1Yh1+QdmOGc+duFpoN4Jb5XVQW2bGFSx4aVBAKthb46IIjTQQN # aO3M8ZzzBDETMmZ0yDZQQYfAb5t8naSSukTBI7o+Qdit4uqGJkoWDizjJn6vNENr # QBce5XFvoeij/pgkIYYu5bMtGPD8yz8DhJDP8ZCSQY7Q1i0skY+9VMurlVDJiGdF # TBXIMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAC # EzMAAAIVGAPTgQcmfFMAAQAAAhUwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3 # DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgwUqAU7BqV81AP/oe # Iw5dCEQ7YJt83QbbAi4ZajVnZiAwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9 # BCBwEPR2PDrTFLcrtQsKrUi7oz5JNRCF/KRHMihSNe7sijCBmDCBgKR+MHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAACFRgD04EHJnxTAAEAAAIVMCIE # IDWQQ7UydSIBtLuXy9Am7uOmaoqxoQP0Fn7KuIDcg2seMA0GCSqGSIb3DQEBCwUA # BIICAG1MLCJe9ECWlEYXEvB+RBXr0z4MkhEI+J6VxSkde8TNnLmsDBIz7mcqFPUu # x8tIYiPYHUaoRsyAbvTMiwHRAZcCyVNOwcWiiyNC8jwh+evZQukA/Ag9mxmNIX2t # M7nrEoOTvgxfmyLKb05GxN3wlTOvpduyzTG3jH5NckENK4Z/xey9eAE/WJ0B/DO0 # LQnTZbIKJbf1x4jfywMf6jfgtowStfkV1ogGUfwgcfKkFm9HomON9YRYGw7Cm5Ru # rAhdZtCwviUwcZGeHQdrtg0Ph1NZgWNRSWnWhFpAOF1Ud8XpiWgPtQ+JHSXn1aQ6 # 3IseFyza+kLpEnL+ywCdihXyF0T32iB6opbefRkDw/6rvXUnAOBEOYA0D7ptMwpW # JATHCtZaHGgAvYeO/4gYQ3lAa9t/DnmHd92cjeS3bVSljVppjfLER7owJ/U8W3+p # spdjnKmGHbWv3Mn+GFM8tqYdzQge0KyxMnxs8VXKHyvSdo7BiusOvOF0TRTJX6K8 # WO4+ClFGT6tk386/Hjb05L4ZVy7z4BP+l2yhQyeey6HckkaV8Wppp78u/FeqhxDc # kglHqfUEBM9QUaEKLP6jZ1UMHuwqplVaxgM118B583qcWCxdb38SNAWyC/rjBAF/ # j14tm6USYyHCyYFayqUlJEQDVE06Dg/pk0A7HFjsGt8EPsHM # SIG # End signature block |