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 # MIIncQYJKoZIhvcNAQcCoIInYjCCJ14CAQExDzANBglghkgBZQMEAgEFADB5Bgor # 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/AUmQN5W4n101cY2L4A7GTQG1h32HHAvfQESWP0xghn+MIIZ+gIBATBu # MFcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIc # +s3Fm+gvfsQAAAAAAhwwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwG # CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZI # hvcNAQkEMSIEIEQ9ghRWoS1bqbKpK7STbJcqqAIptC9CGXc7YxHBfR/xMEIGCisG # AQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAVRAmH3hJypzVdritCRA1 # xocsRGifqkQ2a3UtXhFDk0MfmQPgnl2FWOjywUYMBvGanmptFvrPpkpjdUgpdz2f # tXKmJ6ib/bWypejUgjvktCva/A6OJ3VYybChk5PqWeXdEqRwcKEeqV551I+jeeE7 # NEPwL1OfRu9NmcJlkQUKItFfBRif0fOD4LaTilwtUSd0dsoXy5UM1pmzWe8/4tH0 # ltFAtaN3l+R6699UdmzrvVnvRq0jwpghW0f9lC7BwRTapS/Nx3NKGZHZ0q/9RMzK # QfbPSyGpfvTlQmPDs0LerkiLZ0iVDUBWsi1NjaWvt8nyNqkU0bhEn5u2b825pKXO # 7KGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCCF5gGCSqGSIb3DQEHAqCCF4kwgheF # AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIB # QQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCCXGVJKbQz8HM10bUsj # 1q1lQfnFtamwXQlhmHEgI30wNwIGajWoxjXyGBMyMDI2MDcxNTE4MjU1OS42OTRa # MASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0 # ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo1OTFBLTA1RTAtRDk0NzElMCMG # A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEf4wggcoMIIFEKAD # AgECAhMzAAACFI3NI0TuBt9yAAEAAAIUMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDgxNDE4NDgxOFoXDTI2MTExMzE4 # NDgxOFowgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTAr # BgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUG # A1UECxMeblNoaWVsZCBUU1MgRVNOOjU5MUEtMDVFMC1EOTQ3MSUwIwYDVQQDExxN # aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOC # Ag8AMIICCgKCAgEAyU+nWgCUyvfyGP1zTFkLkdgOutXcVteP/0CeXfrF/66chKl4 # /MZDCQ6E8Ur4kqgCxQvef7Lg1gfso1EWWKG6vix1VxtvO1kPGK4PZKmOeoeL68F6 # +Mw2ERPy4BL2vJKf6Lo5Z7X0xkRjtcvfM9T0HDgfHUW6z1CbgQiqrExs2NH27rWp # UkyTYrMG6TXy39+GdMOTgXyUDiRGVHAy3EqYNw3zSWusn0zedl6a/1DbnXIcvn9F # aHzd/96EPNBOCd2vOpS0Ck7kgkjVxwOptsWa8I+m+DA43cwlErPaId84GbdGzo3V # oO7YhCmQIoRab0d8or5Pmyg+VMl8jeoN9SeUxVZpBI/cQ4TXXKlLDkfbzzSQriVi # QGJGJLtKS3DTVNuBqpjXLdu2p2Yq9ODPqZCoiNBh4CB6X2iLYUSO8tmbUVLMMEeg # bvHSLXQR88QNICjFoBBDCDydoTo9/TNkq80mO77wDM04tPdvbMmxT01GTod60JJx # UGmMTgseghdBGjkN+D6GsUpY7ta7hP9PzLrs+Alxu46XT217bBn6EwJsAYAc9C28 # mKRUcoIZWQRb+McoZaSu2EcSzuIlAaNIQNtGlz2PF3foSeGmc/V7gCGs8AHkiKwX # zJSPftnsH8O/R3pJw2D/2hHE3JzxH2SrLX1FdI7Drw145PkL0hbFL6MVCCkCAwEA # AaOCAUkwggFFMB0GA1UdDgQWBBTbX/bs1cSpyTYnYuf/Mt9CPNhwGzAfBgNVHSME # GDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRw # Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1l # LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsG # AQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p # Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMB # Af8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDAN # BgkqhkiG9w0BAQsFAAOCAgEAP3xp9D4Gu0SH9B+1JH0hswFquINaTT+RjpfEr8Um # UOeDl4U5uV+i28/eSYXMxgem3yBZywYDyvf4qMXUvbDcllNqRyL2Rv8jSu8wclt/ # VS1+c5cVCJfM+WHvkUr+dCfUlOy9n4exCPX1L6uWwFH5eoFfqPEp3Fw30irMN2So # nHBK3mB8vDj3D80oJKqe2tatO38yMTiREdC2HD7eVIUWL7d54UtoYxzwkJN1t7gE # EGosgBpdmwKVYYDO1USWSNmZELglYA4LoVoGDuWbN7mD8VozYBsfkZarOyrJYlF/ # UCDZLB8XaLfrMfMyZTMCOuEuPD4zj8jy/Jt40clrIW04cvLhkhkydBzcrmC2HxeE # 36gJsh+jzmivS9YvyiPhLkom1FP0DIFr4VlqyXHKagrtnqSF8QyEpqtQS7wS7ZzZ # F0eZe0fsYD0J1RarbVuDxmWsq45n1vjRdontuGUdmrG2OGeKd8AtiNghfnabVBbg # pYgcx/eLyW/n40eTbKIlsm0cseyuWvYFyOqQXjoWtL4/sUHxlWIsrjnNarNr+POk # L8C1jGBCJuvm0UYgjhIaL+XBXavrbOtX9mrZ3y8GQDxWXn3mhqM21ZcGk83xSRqB # 9ecfGYNRG6g65v635gSzUmBKZWWcDNzwAoxsgEjTFXz6ahfyrBLqshrjJXPKfO+9 # Ar8wggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEB # CwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYD # VQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAe # Fw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGm # TOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/H # ZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDc # wUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62A # W36MEBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1w # jjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCG # MFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ # 1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP # 8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFz # ymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHz # NgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3 # xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsG # AQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/ # LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEG # DCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29m # dC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYB # BQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8G # A1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQw # VgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9j # cmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUF # BwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br # aS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQEL # BQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfC # cTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AF # vonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l # 9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn # 8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5m # O0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyx # TkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4 # S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9 # y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM # +Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhw # RNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDWTCCAkEC # AQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0 # ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo1OTFBLTA1RTAtRDk0NzElMCMG # A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIa # AxUA2RysX196RXLTwA/P8RFWdUTpUsaggYMwgYCkfjB8MQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T # dGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsFAAIFAO4Bw8EwIhgPMjAyNjA3MTUw # ODMwNTdaGA8yMDI2MDcxNjA4MzA1N1owdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA # 7gHDwQIBADAKAgEAAgImoAIB/zAHAgEAAgIV4zAKAgUA7gMVQQIBADA2BgorBgEE # AYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYag # MA0GCSqGSIb3DQEBCwUAA4IBAQCEMQUzEDlCmzGdxBJJble+/JPLmWG7brLDpJck # UOZz6QpHwoMJkIWrfcRt862NxlRibIF88+G0r/kZkjRVm2tg5F7PJ/rToPZcNynp # u2AuqhSVOYN2e3RN1v70Etx1ndob+UYm4C+oC+oUFg0FCEmrq8FAVeUvgcOfFtiW # Rn7/oS2vTDzZXqqaf98JqySDsk6HrQVSE2R8jRAd2fGv+RXWioQ5m6cLrjK+G0qR # tYALrahgdyGi4i6rufbpDl5pnW3CfEnHvDDE60Ed69KsLK+LEbvythCtPaY18YFp # anBEoDg49VHgTt+yTGiri9hjZcewQPao2KHJOUZixvdE9lOIMYIEDTCCBAkCAQEw # gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAIUjc0jRO4G33IA # AQAAAhQwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B # CRABBDAvBgkqhkiG9w0BCQQxIgQglTaqcsqPqsVlJwCcOYR6vUby7FKhcp/xZFJa # Bo9WF/UwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCA2eKvvWx5bcoi43bRO # 3+EttQUCvyeD2dbXy/6+0xK+xzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAACFI3NI0TuBt9yAAEAAAIUMCIEIIhob0bxl4LT0M/FPkK2 # Yf3IYCDIvFfGU2IB3UJfxSMhMA0GCSqGSIb3DQEBCwUABIICAGpDfYI9uEG2/Zmj # K2gvwWFPllRADqHowebwf9pTWOY6JUZCSLFAWzdO35o82BVLdMB+59MASvzImCNW # dCmqqAA2UxFxi0CgVD77qIToZTYup87MLewUYMD5Ld/quy96CDjgC8dtiwzWpt5N # zYv8hGytOHnbVv/UEn2jELplOCugtgk1bzw/8WN9901MdwtlWdcHuSvYdha6AOuu # +c21ZkAaT1l9LOHrTj/P8Q5RDLBdmE5yLxZmowm3ECFdOmBlDbshuN/v1fHIsngl # K14vji3tW4guJQtou1INHsaPOARtWyVwDhkEttR8qOyT8esdwsPUIg1T+zUcX2S7 # kj7xlWAXiijIcr7x8dFpr1iHbYGeZGVUtgE3j9B9fFAZotU4/zMLlKzSwN5IXfKu # 8zGq53r2msK4pXbHK9YJeNKHA6YEGKm4N5YXKR4KCSAIVrMSiKLkw5+ovj7xdwzS # yhkG1M7SZrd9fcmfpsGRzTlWlGKuqMERx4+KcaeKTrz/BNxuU64VGR7Og1c9abGN # SH3Ht0qQdWttUT3+HvMV8Fre5atTnRXoNEIA34PcqQmG7yQ4bSJY32vY1qi9tnW0 # DH55yTXAS8riiyVmtwr0VUvbFbuzwpwUnCvsQQo70DlmvnXlQLQy7INiZVwL9PqA # yktC0dRgFsB2k5G2RKD4AIAyCXy9 # SIG # End signature block |