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 # MIInSQYJKoZIhvcNAQcCoIInOjCCJzYCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB9tbyCRMkXzT1y # jVAKvdb+ZVMgucYJzBWrueLXsnxdW6CCDLowggX1MIID3aADAgECAhMzAAACHU0Z # yE7XD1dIAAAAAAIdMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQzWhcNMjcwNDE1MTg1 # OTQzWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDQvewXxx9gZZFC6Ys1WBay8BJ8kGA4JQnH5CMafqOASlTpK9H8 # o5ZXTXt0caVQTNMUPt445wXYD+dFtaKWTwDn1I52oUSrC9vJin1Gsqt+zyKJL5Dg # 3eQXbQNR61DmMy20GLTIO3SFed9Rfi/ophgCLGFLDR3r0KvHjwMb/jYWS0celV/4 # Lz27LfAekm8v9E5IXaeiXbAUYZKK090n4CVl3JBtbN+9DtI9SNu/yjvozW52/u7R # X/Ttpa/KDlpuokZ+Zcbvmtd9ur9gFLvZzh41o9MsE/clQtdaFWGvuo6Jua/ntpgk # ey3E5/vBFe+MJPG6phdnuo6r57ZudCudiI1bAgMBAAGjggGbMIIBlzAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFH6QuMwqcPG0hQlQ6c5jCtTTLrVeMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQL # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAxMis1MDc1NTkw # HwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEwYAYDVR0fBFkwVzBVoFOg # UYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNybDBtBggrBgEFBQcBAQRh # MF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBKTbYOjzwTG/DXGaz9 # s6+fQeaTtDcFmMY+5UyVFCyj7Pv+5i37qfX8lSL/tBIfYQfWsMuBQlfZurJD6r4H # VJ2CeH+1fgiq8dcHdVKoZ3Sa2qXoX3cq9iS8cVb06B7+5/XJ7I0OxHH9fDsvJ3T3 # w5V/ZtAIFmLrl+P0CtG+92uzRsn0nTbdFjOkLMLWPLAU3THohKRlSEMgFJpPkm5n # 5UAZ35xX6FWCrDLsSKb555bTifwa8mJBwdlof0bmfYidH+dxZ1FdDxvLnNl9zeKs # A4kejaaIqqIPguhwAti5Ql7BlTNoJNwxCvBmqW2MQLnCkYN/VVUsR3V2x/rcTNzo # Bf/Z/SpROvdaA2ZOOd1uioXJt3tdLQ7vHpqpib0KfWr/FWXW10q38VxfCnRQBqzb # SuztR7nEMuzX7Ck+B/XaPDXd1qh72+QYyB0Z2VzWmO9zsnb9Uq/dwu8LGeQqnyu6 # 7SDGACvnXii2fb9+US492VTnXSnFKyqwgzUyFMtZK1/sHYTv6bG4TtQUygQxTN+Z # V+aJIlKO2MqZ7bKrAnOzS9m6NgoTdWOq11bTOZwKlIEV/EhV9SWkDmdpR/hPPT2v # 6TEj4F8PT/zHjRezIU5c/DGlt/VhY/pK0XkJtEyMmmS1BMtjU/rqBZVMIm3dnxQs # /TBByr+Cf8Z1r7aifQVQ+WSqzjCCBr0wggSloAMCAQICEzMAAAA5O7Y3Gb8GHWcA # AAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoXDTM2MDMyMjIyMTMwNFow # VzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo # MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAyNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeqlRYHNa265v4IY9fH8TKh # emHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo0dtS/EW6I/yEL/bLSY8h # KpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATvQVL4tcf03aTycsz8QeCd # M0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a1uv1zerOYMnsneRRwCbp # yW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1FyQfK0fVkaya8SmVHQ/t # Of23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfOGSWHIIV4YrTJTT6PNty5 # REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7ttOu1bVnXfHaqPYl2rPs # 20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJuz2MXMCt7iw7lFPG9LXK # Gjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxSCwyoGIq0PhaA7Y+VPct5 # pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOmVQop36wUVUYklUy++vDW # eEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3SkE/xIkgpfl22MM1itkZ # 35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGC # NxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPXLQaUEggxMBkGCSsGAQQB # gjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU # ci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2Ny # bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAFJQfOChP7onn6fLI # MKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D5W4wMwYeLystcEqfkjz4 # NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBYnbu0+THSuVHTe0VTTPVh # ily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSIvgn0JksVBVMYVI5QFu/q # hnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6aR9y34aiM1qmxaxBi6OU # nyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4wPKC5OmHm1DQIt/MNokbb # H3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7RTX8AdBPo0I6OEojf39z # uFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK/fg8B2qjW88MT/WF5V5u # vZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSKYBv0VisCzfxgeU+dquXW # 9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkwYTu/9dLeH2pDqeJZAABV # DWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVTQl0v4q8J/AUmQN5W4n10 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghnlMIIZ4QIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIEQ9ghRW # oS1bqbKpK7STbJcqqAIptC9CGXc7YxHBfR/xMEIGCisGAQQBgjcCAQwxNDAyoBSA # EgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20w # DQYJKoZIhvcNAQEBBQAEggEAMenmWO+Lp8lVNCtStdQllqBdcQwlauP4Ba5VRww2 # T64ASUArMbvETmyM2zjYBGRjMdPsOqSic0PZihTXnk3lW6Eo4le4qQWUrK6/+O0R # VLdicfqLCF/kCzEwzR1h7v89q8DKH0cGco+sQK9i5P/CshTKAaXoTSqC1uJ3RIJH # SAg7oea7t2BavFm10v6gMk4aoeSoncxaMgJ4ySUK3UAkfg+TCz9RjXRBp5JkhRT0 # o9bld0WILJ08fO+kMkTXCkcxq1nCKLJmBxOseqgchCy/acvXBv9QtAZ2vpfLjfWA # h00b6lnUzye3K/M3gqpc2Mh42BvCpNk7Wj+k3SSCF76P56GCF5cwgheTBgorBgEE # AYI3AwMBMYIXgzCCF38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUD # BAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoD # ATAxMA0GCWCGSAFlAwQCAQUABCDsWQE+n33tTuUgNi5DnCRcdtzEThHpwoNUaPuZ # 0+lRLAIGajH/ArXHGBMyMDI2MDcwNzE2NTYzMC45MTNaMASAAgH0oIHRpIHOMIHL # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxN # aWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRT # UyBFU046MzMwMy0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFNlcnZpY2WgghHtMIIHIDCCBQigAwIBAgITMwAAAiEzwDX70g8hpAABAAAC # ITANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAe # Fw0yNjAyMTkxOTM5NTRaFw0yNzA1MTcxOTM5NTRaMIHLMQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmlj # YSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046MzMwMy0wNUUw # LUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDbcTACqU1YvRocyWL2PL9fyf/+ # ULs2qK7U1aZsRnDZSnlCr7K7jgA3eFCEJL5BZ7dUTC0DeZepf+ZC+7HEbB4IdzmJ # fQAUDFFerqY5VTHmQvP2XA3lWSFj740idcGUHglP5H/PbCJU7GAHWP2HdcCjdx1l # YAo0A+zLI7xwnTQeMyOXX212Eg4UmDPPJgxdTMw6WFVWsBPWRBi5gDixy2s+7R8A # Dk5lbBBFDB5h0CjrNWIN7uCAzF5g7trrL8nXIKp10mj9RxhcGQ+tlht6VIvdygRV # TUGdzFB2/nBvJqQ9kxxFltQST70fEdx4TyaKow/f5+BSh4z4/9f7NXIVVTLn/8kc # JAfRqFmRrrFt3IKby7VrzmYuoQWD0lmNFtGQ57BrJkPrPFAPek1ALtcbb7FH3nQp # vi8ngz/MFX/+cnmNFWFU29VVLmzB9XvLZxbYvkeett0mh5lfteeN2rEwUyrdrKuf # z9h2S6pbate+C2h02CrXwSka0x6ezpTmGkIJLFt25ub/UYXNLdHdsxGD6EfckOIo # JYsm4MS9F/vSqLNHK89I0vTLBngQEp6LIFkINanRT3PtNx3pNKRKJRALc6L6mhW4 # hL4aHL749qPfQ72t5qAMm5xiKYMgJ2WanidRLNuI251JIN7raaeA/2vb0XFkZcIb # TR1pfQGsco4U0g5tjwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFOYjIs5qa6pfuquP # yyK1FTr5QDCnMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1Ud # HwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3Js # L01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggr # BgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNv # bS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIw # MTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgw # DgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQA4I/3bkdnTxD2rFum3 # MF8xVKdEkohAObbePrQ+0fr5bRimjz9sVkKT/7gcj4OMcClSYG+IdX6Mp3EYsLHW # fjvwfzFoeZE+yTbdBj/1VHZQRuCmw6QqeVCTbw2nnS7nBxnWd9oZXbPUpqEawH5D # qXQaWFgR9A4KWVK/IvXVDMj1PlPCES1P3JonNbdhkkkz49rJuKOm5b7e/BH8loqA # mXOXRc22yxWVTMWrEp4pslmv8eT7VoY8X/jdKYTPVEXsfmLbVFcqzMuB8vFGfUyW # sWROS8wgq7lQYfWcYqh7NymoATX+wWYK3zWG7aRciPGUAzznXdf+aHtIWnQLNa5H # FmSXkiak3fSuprWYZiHhuYjE16hroApcBHpm+8S/kNqhm9WjQX+2BxnYv+Jejy6l # qTi8fLBLS069WXVw/ptf5IV+FtYl34GvVoeg31UoUmVVZe1SDUJkm9dDXc8l/qBD # YiAIT2CCsPTyt9XA9JVuHxdP63n7ChvWAO/47QRuCDsUlFJoWwyBwl7jeYpaRVMt # Qt0iuJMGGjgEaJX1Q/2j8sXURvTceLHDD9ipWt092ZDWMQciDRmhHNFOX1dnjBvk # /k1UMcg997j5oYznAnSpJvlg/4BP3aVE0h/YH2KgsKbU4NXZHAjJXj2Slqo1C115 # CG6qBZaFkM8W6vPZCm5qnSezOjCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkA # AAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVow # fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd # TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX # 9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1q # UoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8d # q6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byN # pOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2k # rnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4d # Pf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgS # Uei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8 # QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6Cm # gyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzF # ER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQID # AQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQU # KqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1 # GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0 # bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMA # QTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbL # j+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1p # Y3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0w # Ni0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIz # LmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwU # tj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN # 3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU # 5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5 # KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGy # qVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB6 # 2FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltE # AY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFp # AUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcd # FYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRb # atGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQd # VTNYs6FwZvKhggNQMIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjMzMDMtMDVFMC1E # OTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEw # BwYFKw4DAhoDFQALbEgZZnyYHXJ1DGb5fGjplXptuaCBgzCBgKR+MHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7feBgTAiGA8y # MDI2MDcwNzEzNDUzN1oYDzIwMjYwNzA4MTM0NTM3WjB3MD0GCisGAQQBhFkKBAEx # LzAtMAoCBQDt94GBAgEAMAoCAQACAkIuAgH/MAcCAQACAhK6MAoCBQDt+NMBAgEA # MDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAI # AgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBAAJLMf1Gitylnq+rQvzTg6hNgec8 # EZ5qCPzxlK/fdK+FQQ6cOTculLroDlCmkExB3u5b4MPQFBymJlvi5iUnuj6zUmbX # Wb340CJFuejGnKGBFAd/3+jEnK590sJuju7bYkWMjRQ4XV9S3MVKatDM0mrFLGMn # ZQEYAWvi08Xp0/EL/qjyQV+cN0US+eIRZaV4I/0Qg2sVc0tt885C6excF/AhZnqp # m8bheMKReJmN7m6AhcBnpF9P6eJ61PnseWTN8drxc6K0evYacIIa6ryVOCPYRzgk # 36gNQWZRXgGBN2/5tcelk1tVpys2cNpYYJVbB+aKCNPdW2ZHA7kKMQcuTX0xggQN # MIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAiEz # wDX70g8hpAABAAACITANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0G # CyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDHOUIH0SBtd3bjPxGgtdVXsfiS # WmhWJ0CPBmL1OeEFOTCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIADvIQef # FVUa4BJy8IZywMAvmGSKdUVqEmy9A++PCj1EMIGYMIGApH4wfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTACEzMAAAIhM8A1+9IPIaQAAQAAAiEwIgQgEmOeFgFi # 09JxkvKaz7592StVlrmW85SDR5R/mxsTzsMwDQYJKoZIhvcNAQELBQAEggIAlwZJ # OiORFG2sglp4xhnK9xSQXV3RrZUG2m6S2r2jwPSHY2O+iVCgbJXaqpdLB//gT+hC # CVMgIijX3FuE02Mq7b1nFD70WF226djJJTTRQmIlcyII0FaymYwcN3UcjC8vYJl9 # cmWxlHX4wKCXOH+Hu8M8bwDggixGzVXy56htPNOIqebtUBK38jjnEZsABiX4ul5w # zCYHI7xOQ+rvarM9MLhTxeSBMk2GjnIFXm395R1NHsjfJuHENryH6yJlE6NZvq2Q # DtkuXSDrc8KLfAz14HBPcCQz7kvWTulhn8M3Wunlht3tkKAZl6KSm7+80nmZjd0W # mz2stiiH4bB/4x09VzekawmfTX+rjXkfB1sWFyRsDygJjAD2HyZqSHKII6veEbXZ # 8an+Y/n+eVKend0UAJnaHQXVKs53a0pDLl52KcwXOyhT3SwxUnTZPG1Wq3v+9MEy # sObFlmEAHa6kgjlGY/Tsv2V2dG810w1ye95IjQqTJYd9TS3HeNp+RFLbqVU2sDWX # SvjUzepom+BSlmgFYSLcMNQfWXSpbVN3WvDbuoCqoTGh3j8BgzFOnwSk5OCEZaA4 # ByPo/Gznn6quGsRdsdvy1DKu8RfTlFjsnxx82dNnqERYxZNg1aumL1jO2D3I7cIJ # PWAZm/thNhkpqlyu0+FXi/zjthMkbdDY2rUgdLU= # SIG # End signature block |