Public/AzStackHci.PageFileSettings.ps1
|
# /////////////////////////////////////////////////////////////////// # 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 } } $backupObject | ConvertTo-Json -Depth 3 | Out-File $BackupFile -Encoding UTF8 -ErrorAction Stop $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" $backupObject | ConvertTo-Json -Depth 3 | Out-File $BackupFile -Encoding UTF8 -ErrorAction Stop 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 } } } $backupObject | ConvertTo-Json -Depth 3 | Out-File $BackupFile -Encoding UTF8 -ErrorAction Stop # 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 } } } $backupObject | ConvertTo-Json -Depth 3 | Out-File $BackupFile -Encoding UTF8 -ErrorAction Stop # ── 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 # MIInRgYJKoZIhvcNAQcCoIInNzCCJzMCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBo5CTjKS2D84hZ # FtBMeX2ddKtbdT6t+ImvUeBKGVlgWKCCDLowggX1MIID3aADAgECAhMzAAACHU0Z # 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 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghniMIIZ3gIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIKEt/Fw5 # cWfVt65BD74JZvBLNw5e/h9MzlBUK+lPD6zZMEIGCisGAQQBgjcCAQwxNDAyoBSA # EgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20w # DQYJKoZIhvcNAQEBBQAEggEAtT7xt/KMGatp32M/go19/cAN0Czm1Pd/b+gzM2Yt # xGhVU6TGvm+Y7TsIy4vL/X2gYoZawVatZEPFtg8EOcSl1iWukacaK5OpXmtKzA++ # TssAq+InM+DS7dT4hMGpPgrf2TnuVHWYX7qTerJ8Y2eGnf2RpjN0lYPsJ1vgGG9s # 5e76oPioSspqWANhFnN2JAvGMbKGEcvJtn4zwRU9+Fq0M861L6ENxnTFfiBxSqeA # uNfOOAgVZw7WeVkUKkXKmE7jDSpdt+3BE901tl3a9TfBNOdaxPsDBRxiarvfYfOW # VabIYlO/ID6NbuyT8cRjIyEJFdt9a1Fsfe7WEoWNJM1TFaGCF5QwgheQBgorBgEE # AYI3AwMBMYIXgDCCF3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUD # BAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoD # ATAxMA0GCWCGSAFlAwQCAQUABCDReRkHbQQ5nh0BK8XJWy7n4ny4A5v0NGwy5Rgp # xNb80gIGaeduuHW1GBMyMDI2MDQyODIxNTI1NC4xNjNaMASAAgH0oIHRpIHOMIHL # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxN # aWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRT # UyBFU046QTkzNS0wM0UwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFNlcnZpY2WgghHqMIIHIDCCBQigAwIBAgITMwAAAifVwIPDsS5XLQABAAAC # JzANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAe # Fw0yNjAyMTkxOTQwMDRaFw0yNzA1MTcxOTQwMDRaMIHLMQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmlj # YSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTkzNS0wM0Uw # LUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDixWy1fDOSL4qj3A1pady+elID # LwnF3UuLzJIOWwGHcEgrxxwtnyviUIDmmxylTUl1u+2rBPp2zT4BwwQhvGaJpExq # vPLlDFlbfmSflKI86eFqofiZ7j8NTRO4l7wGg9Njm+muNauTcFW2qdfIjKE950Ok # rm9MnMOGYy+fibNYdxTPRPq1T4MLZK3s3vdMyMEOldcOQkSKpxD6/1Gk6gOmCu2K # gI8f0ex6vYxnKDl9W0OLSEa/6y82oIbsm+1QBifOQ47xWKTG1CmvtGr85LzA75/M # AcUmRw5/of/qET0UFV1WulMcJrI6DASAsNCNB+6WLrotuBZAj+VMlqbn5RMZ6Q4I # Y7JwaAiIXh7VjxrnwUOYZG8WEGhfrA98di+7LEn9AqvvEOyG+UQcjVhCCbMGXigJ # XSApeyeWupCsD0jgQMNCxfB5BLBDWxgdY3dJBEPgxfkgTDQLBggtVv2d5CYxHKgI # ItB4bI5eSb5jkIG2WotnFetT0legpw/Eozwf39ao6tENY21eVWIzRw/GsmvwjYQF # 6vVrxOD0pGVsfqGF8s3VPeY7hI2TxHFMqNA0IB/a2NLY7JTxYAKAP/11EJZt7xbq # DLMgD1YDdGEzGpQijm3nAPCL2CebP/jmu90abJ2W425yglGHTI/nCBrwSpfRCgwz # rfFelJaCKM6+35aFfwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFNLW58N4MGSG6ud7 # jWqgT92orfReMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1Ud # HwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3Js # L01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggr # BgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNv # bS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIw # MTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgw # DgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQAqncud4PSC1teb2H6n # Ruy7sDiKK13FXJirVB4Tfwjdo2Mb+QL4j7wZ/k4G9P0CANHZFrDQcK0VFDTysrYu # 8Z0Aha14acDZPsyIoPvAGRRhaHEuf7NckRjkfa/ylo1KyII8jbL9N9sJAqBPL8V4 # FNBjljv+1GHDOw127rZz5ZSTPoAPb2SA0v5yDgcpUMfxglPyp6cnPPoQpTtD9OGx # 8Dwm2P+o1TPxBIy6I0T9RauulogVCvKwflfeLTcKAvnSG1rCjerSXmU1DNXOsAD/ # bsrSjgbX5mAbD7XTRMF/vawAWESFcn/BjjizxeWZb00aYSlkJA2rVtFlMM481aVW # XdAbXPP5RzUiWTlgyHf/G7lCxHYWGIZuB13T3aI6Y8mEgn/ou40aiFJo8r0+i0P5 # GdNneWtxiR0CMKUfko+5s/73cwe1Wfp8BKXa270cicVQasFf5sRV7pFm+V7fNRXw # Cu7anTOmga76zO7/2t+zOlibvphT+Q6Zd+B2qYsSn4xBaY+YzHpnycLW5cvJyhPx # BCcb1oRYfhRzCADb2utI2EtGCjc2P2ii4LyR4QMb/n8cOweL9IqVTKKzzVk+zZJx # V3vrp4LyuQXw0O30la6BcHdNAAAB9UC83zs3G9d+AlIfZLM97tMUNKWjbBpIirFx # 6LTDFXVtZQd7hqzLYByjbjH0ujCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkA # 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 # VTNYs6FwZvKhggNNMIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkE5MzUtMDNFMC1E # OTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEw # BwYFKw4DAhoDFQAjHzqthPwO0GDckDMA6x54lIiMKqCBgzCBgKR+MHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7ZsmkTAiGA8y # MDI2MDQyODEyMjkwNVoYDzIwMjYwNDI5MTIyOTA1WjB0MDoGCisGAQQBhFkKBAEx # LDAqMAoCBQDtmyaRAgEAMAcCAQACAg3ZMAcCAQACAhKcMAoCBQDtnHgRAgEAMDYG # CisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEA # AgMBhqAwDQYJKoZIhvcNAQELBQADggEBAADxaShmuslRyo5i2ps0OQRm9WlJov0c # uy3MQD37Z1sBGOVRhgZ8xmUpZ+NrLUEeS1fABlJ1/kvbNTmIhys3hhDhp+fa5Htk # 7SIh/DTWyqpPUkBoG4m19mCZkh7boG4ECb4uNflaJW58IuuJ1+rhFc8qtcWSq7Op # AYQ6LetWEGZRziCfzxpXRq7AqNKC0n7cqGtPiwn0xowBnQOBGY/ceswZhKQmxR1N # 3q+cFyANAkqUSyshS/+pfgnPN2C6lVp6JHc7VEhndTEIMmF8seyTv8qZBoLtbqZl # pXbWFp1XVb6d3YvdhSWm/disX8xLEA/MzOhvxUHijr5TZyCPdiRcTtYxggQNMIIE # CQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYw # JAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAifVwIPD # sS5XLQABAAACJzANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqG # SIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCBZtQAReaBEN1Y6FJwMUSmdmJPTbW2 # rdXLjruEJovnPzCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIOXnARo1oVIc # OLJKDqlE0adq/jZ9TXdlnXWRcXGThBFyMIGYMIGApH4wfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTACEzMAAAIn1cCDw7EuVy0AAQAAAicwIgQgrS5KvG947snZ # 2a7CwSeVX7GFIINFq+e/sKUFZnvMj2QwDQYJKoZIhvcNAQELBQAEggIAvedry6Kq # 9O24gGhQPgJSf6qZVK1Vaop+ZZjSozURyZnp+xqJMQKIfLzYjyyRlTx9AD+yU7gO # 6FTxGyv35h4gXhiUv7LhKxmf02pOEbENMml4GRg6ZRB/KuCevoIFbW9/C2usGYD2 # 0YtSjTQU8A6hR/bMxKPhFi71inWAXPSSChvH5FpUVmMpQtSJVAVvWd74hjm2KPic # KRAAJ5hTzQFlvb4qku9XGHmcHQCR+6M6VFXyMtAmRKuy+/3MeVfFDGs8DRgXqypW # gm8tupC43XSMMMwu5Qd+yI0ODmdWqZS8OFllx3Q7zBV1g83QCnRfR6CaANZsDYEG # 8Wd8O0T1h7RBG1MVZz+wA4OnHu/1Kt/UNt9bwRdk3aN9U7Ym1nRhVvmoFCBd9Rez # V9oOtIubn31H+5O6gA5aeoQqjgDjOZw0/V0ULs5BBFLMcW5gog0j5Dqi7BPhkGUg # eyGUkxG4QErsHTds/LQaMv9M9JunwISQjCsvnz8qNzxWsJEC/i74tBvnFZtejnTv # yHSNn73nY1aAMZsUAg31T8EzTEZFUuvTaG1vC5jI8jUW7ozNOX8GSxaHkGxx3Oq7 # ZsOnHoXXQSLyjVdx44J4tJIKE3G2lCT5BG34tDE/wbtzWtAK4iPHO7EYjsn/3DKr # 5g/hBcN4DnkP3aRdlLlzME2j5Nx/zgZ/IQc= # SIG # End signature block |