packages/AzStackHci.DiagnosticSettings.0.6.8/Public/AzStackHci.PageFileSettings.ps1
|
# /////////////////////////////////////////////////////////////////// # Strict Mode v1 (PS 5.1 safe) - surfaces reads of uninitialised variables at runtime. Set-StrictMode -Version 1.0 # Get-AzStackHciPageFileSettings Function # Used to get information about the current page file settings # on the system. # /////////////////////////////////////////////////////////////////// Function Get-AzStackHciPageFileSettings { <# .SYNOPSIS Gets current page file settings .DESCRIPTION Queries the system for current page file settings. Use -Scope Cluster to collect settings from all nodes in the failover cluster. #> [CmdletBinding()] [OutputType([PSCustomObject[]])] param ( # Scope: 'Node' (default) for local node only, 'Cluster' to collect from all cluster nodes [Parameter(Mandatory=$false)] [ValidateSet('Node','Cluster')] [string]$Scope = 'Node', [switch]$NoOutput ) begin { # Requires administrator permissions to get page file settings and write to log file to disk if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } $DateFormatted = Get-Date -f "yyyyMMdd-HHmm" if(-not(Test-Path "C:\ProgramData\AzStackHci.DiagnosticSettings\")) { try { New-Item "C:\ProgramData\AzStackHci.DiagnosticSettings\" -ItemType Directory -ErrorAction Stop | Out-Null } catch { Throw "Failed to create output directory 'C:\ProgramData\AzStackHci.DiagnosticSettings\': $($_.Exception.Message)" } } } process { # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active $script:SilentMode = $false # Handle -NoOutput: suppress all console output if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # --------------------------------------------------------------- # Cluster scope: fan out to all cluster nodes via Invoke-Command # --------------------------------------------------------------- if ($Scope -eq 'Cluster') { if (-not (Test-CommandExists Get-Cluster)) { throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node." } try { [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop } catch { throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)" } Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose # Self-contained scriptblock — does NOT depend on the module being installed on remote nodes $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock { $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Error = $null } try { $DateFormatted = Get-Date -f 'yyyyMMdd-HHmm' $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' if (-not (Test-Path $BackupDir)) { New-Item $BackupDir -ItemType Directory -Force | Out-Null } $BackupFile = "$BackupDir\PageFile_Settings_$DateFormatted.json" [bool]$PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile $PageFileConfig = $null if ($PageFileAutoManaged -eq $false) { $pfSetting = Get-CimInstance -ClassName Win32_PageFileSetting if ($pfSetting) { $PageFileConfig = @{ Name = $pfSetting.Name InitialSize = $pfSetting.InitialSize MaximumSize = $pfSetting.MaximumSize Caption = $pfSetting.Caption Description = $pfSetting.Description SettingID = $pfSetting.SettingID } } } $PageFileUsage = Get-CimInstance Win32_PageFileUsage $backupObject = [PSCustomObject]@{ BackupDate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Node = $env:COMPUTERNAME PageFileAutoManaged = $PageFileAutoManaged PageFileConfig = $PageFileConfig PageFileUsage = @{ AllocatedBaseSize = $PageFileUsage.AllocatedBaseSize CurrentUsage = $PageFileUsage.CurrentUsage PeakUsage = $PageFileUsage.PeakUsage Name = $PageFileUsage.Name } } [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false))) $result.Success = $true $result | Add-Member -NotePropertyName PageFileAutoManaged -NotePropertyValue $PageFileAutoManaged $result | Add-Member -NotePropertyName PageFileName -NotePropertyValue $(if ($PageFileConfig) { $PageFileConfig.Name } else { $null }) $result | Add-Member -NotePropertyName PageFileInitialSize -NotePropertyValue $(if ($PageFileConfig) { $PageFileConfig.InitialSize } else { $null }) $result | Add-Member -NotePropertyName PageFileMaximumSize -NotePropertyValue $(if ($PageFileConfig) { $PageFileConfig.MaximumSize } else { $null }) $result | Add-Member -NotePropertyName PageFileAllocatedBaseSize -NotePropertyValue $PageFileUsage.AllocatedBaseSize $result | Add-Member -NotePropertyName PageFileCurrentUsage -NotePropertyValue $PageFileUsage.CurrentUsage $result | Add-Member -NotePropertyName PageFilePeakUsage -NotePropertyValue $PageFileUsage.PeakUsage } catch { $result.Error = $_.Exception.Message } $result } -ErrorAction SilentlyContinue -ErrorVariable remoteErrors # Report per-node results if ($remoteErrors) { foreach ($err in $remoteErrors) { Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose } } $successCount = 0 $failCount = 0 foreach ($nodeResult in $remoteResults) { if ($nodeResult.Success) { Write-Verbose " $($nodeResult.Node): SUCCESS - backup file created" -Verbose $successCount++ } else { Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)" -Verbose $failCount++ } } Write-Verbose "Cluster collection complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose if ($failCount -gt 0) { Write-Warning "$failCount node(s) failed. Review the output above for details." } # Build PSCustomObject array with settings from all nodes $outputObjects = foreach ($nodeResult in $remoteResults) { [PSCustomObject]@{ Node = $nodeResult.Node Success = $nodeResult.Success PageFileAutoManaged = $nodeResult.PageFileAutoManaged PageFileName = $nodeResult.PageFileName PageFileInitialSize = $nodeResult.PageFileInitialSize PageFileMaximumSize = $nodeResult.PageFileMaximumSize PageFileAllocatedBaseSize = $nodeResult.PageFileAllocatedBaseSize PageFileCurrentUsage = $nodeResult.PageFileCurrentUsage PageFilePeakUsage = $nodeResult.PageFilePeakUsage Error = $nodeResult.Error } } return $outputObjects } try { # Get current page file settings [bool]$script:PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile } catch { Write-Verbose "Failed to get current page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Failed to get current page file settings. Error: $($_.Exception.Message)" } # Collect page file configuration $pfConfig = $null if (-not $script:PageFileAutoManaged) { Write-Verbose "Current Settings: Page File automatic management is Disabled" -Verbose try { $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting } catch { throw "Failed to get page file settings: $($_.Exception.Message)" } if ($script:PageFileConfiguration) { $pfConfig = @{ Name = $script:PageFileConfiguration.Name InitialSize = $script:PageFileConfiguration.InitialSize MaximumSize = $script:PageFileConfiguration.MaximumSize Caption = $script:PageFileConfiguration.Caption Description = $script:PageFileConfiguration.Description SettingID = $script:PageFileConfiguration.SettingID } Write-Verbose " Name: $($pfConfig.Name), InitialSize: $($pfConfig.InitialSize), MaximumSize: $($pfConfig.MaximumSize)" -Verbose } } else { Write-Verbose "Current Settings: Page File automatic management is Enabled" -Verbose } # Collect page file usage try { $script:PageFileUsage = Get-CimInstance Win32_PageFileUsage } catch { throw "Failed to get page file usage: $($_.Exception.Message)" } $script:PageFileAllocatedBaseSize = $PageFileUsage.AllocatedBaseSize $script:PageFileCurrentUsage = $PageFileUsage.CurrentUsage $script:PageFilePeakUsage = $PageFileUsage.PeakUsage Write-Verbose "Page File usage: AllocatedBaseSize=$($script:PageFileAllocatedBaseSize), CurrentUsage=$($script:PageFileCurrentUsage), PeakUsage=$($script:PageFilePeakUsage)" -Verbose # Save backup as JSON $backupObject = [PSCustomObject]@{ BackupDate = (Get-Date -Format "yyyy-MM-dd HH:mm:ss") Node = $env:COMPUTERNAME PageFileAutoManaged = $script:PageFileAutoManaged PageFileConfig = $pfConfig PageFileUsage = @{ AllocatedBaseSize = $PageFileUsage.AllocatedBaseSize CurrentUsage = $PageFileUsage.CurrentUsage PeakUsage = $PageFileUsage.PeakUsage Name = $PageFileUsage.Name } } $BackupFile = "C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_$DateFormatted.json" [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false))) Write-Verbose "Current Page File settings exported to '$BackupFile'" -Verbose Write-HostAzS `n # Return a PSCustomObject for easy reporting [PSCustomObject]@{ Node = $env:COMPUTERNAME Success = $true PageFileAutoManaged = $script:PageFileAutoManaged PageFileName = if ($script:PageFileConfiguration) { $script:PageFileConfiguration.Name } else { $null } PageFileInitialSize = if ($script:PageFileConfiguration) { $script:PageFileConfiguration.InitialSize } else { $null } PageFileMaximumSize = if ($script:PageFileConfiguration) { $script:PageFileConfiguration.MaximumSize } else { $null } PageFileAllocatedBaseSize = $script:PageFileAllocatedBaseSize PageFileCurrentUsage = $script:PageFileCurrentUsage PageFilePeakUsage = $script:PageFilePeakUsage Error = $null } } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Get-AzStackHciPageFileSettings function" } } # End of Get-AzStackHciPageFileSettings # /////////////////////////////////////////////////////////////////// # Set-AzStackHciPageFileSettings Function # Used to set a manual page file on the system with a specified size, # or auto-sized for kernel dump support. # /////////////////////////////////////////////////////////////////// Function Set-AzStackHciPageFileSettings { <# .SYNOPSIS Sets page file settings to a specified size or auto-sizes for kernel dump support .DESCRIPTION Configures a fixed-size manual page file, disabling automatic page file management. Use -KernelDumpRecommended to auto-size the page file based on system memory, using the same calculation as Set-AzStackHciMemoryDumpSettings (64 GiB for <768 GB RAM, 128 GiB for >=768 GB RAM). This ensures the page file is large enough to stage a kernel memory dump. Or specify -InitialSizeMB and -MaximumSizeMB for explicit sizing. Use -Scope Cluster to apply settings to all nodes in the failover cluster via Invoke-Command. The scriptblock is self-contained and does not require the module on remote nodes. Reference: https://learn.microsoft.com/en-us/windows-server/administration/server-core/server-core-memory-dump #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([PSCustomObject[]])] param ( # Path to drive letter for PageFile [Parameter(Mandatory=$true, Position=1)] [ValidateScript({Test-Path $_})] [String] [ValidatePattern('^[C-Zc-z]:\\?$')]$PageFileFileDriveLetter, # Auto-size for kernel dump support (uses same calculation as Set-AzStackHciMemoryDumpSettings) [Parameter(Mandatory=$true, ParameterSetName='KernelDumpRecommended')] [Switch] $KernelDumpRecommended, # Initial page file size in MB (manual sizing) [Parameter(Mandatory=$true, ParameterSetName='ManualSize')] [ValidateRange(1024, 262144)] [uint32]$InitialSizeMB, # Maximum page file size in MB (manual sizing) [Parameter(Mandatory=$true, ParameterSetName='ManualSize')] [ValidateRange(1024, 262144)] [uint32]$MaximumSizeMB, # Scope: 'Node' (default) for local node only, 'Cluster' to apply to all cluster nodes [Parameter(Mandatory=$false)] [ValidateSet('Node','Cluster')] [string]$Scope = 'Node', [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")] [switch]$NoOutput ) begin { if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } # Backup current page file settings (skip in Cluster mode — each node backs up inline) if ($Scope -ne 'Cluster') { $null = Get-AzStackHciPageFileSettings } } process { # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active $script:SilentMode = $false if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # Determine target page file size if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { # Use same calculation as Set-AzStackHciMemoryDumpSettings / SetDedicatedDumpFileSize [uint32]$TargetSizeMB = SetDedicatedDumpFileSize if (-not $TargetSizeMB) { throw "Failed to calculate recommended page file size based on system memory." } $InitialSizeMB = $TargetSizeMB $MaximumSizeMB = $TargetSizeMB } if ($MaximumSizeMB -lt $InitialSizeMB) { throw "-MaximumSizeMB ($MaximumSizeMB) must be greater than or equal to -InitialSizeMB ($InitialSizeMB)." } $targetSizeGiB = [math]::Round($MaximumSizeMB / 1024, 1) # --------------------------------------------------------------- # Cluster scope: fan out to all cluster nodes via Invoke-Command # --------------------------------------------------------------- if ($Scope -eq 'Cluster') { if (-not (Test-CommandExists Get-Cluster)) { throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node." } try { [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop } catch { throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)" } Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose $sizeDescription = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { "Set kernel-dump-recommended page file (${targetSizeGiB} GiB) on $($Nodes.Count) cluster nodes" } else { "Set manual page file (Initial: ${InitialSizeMB} MB, Max: ${MaximumSizeMB} MB) on $($Nodes.Count) cluster nodes" } if (-not $PSCmdlet.ShouldProcess("Cluster '$ClusterName' ($($Nodes -join ', '))", $sizeDescription)) { return } $remoteArgs = @{ DriveLetter = $PageFileFileDriveLetter InitialSizeMB = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { 0 } else { $InitialSizeMB } MaximumSizeMB = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { 0 } else { $MaximumSizeMB } AutoSize = ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') } $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock { param($Params) $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Action = $null; PageFileSizeMB = 0; Error = $null } try { $DriveLetter = $Params.DriveLetter if ($DriveLetter.Length -eq 2) { $DriveLetter = "$DriveLetter\" } # Calculate size if auto-sizing # Tiers: <=128 GiB → 32 GiB, 129-767 GiB → 64 GiB, >=768 GiB → 128 GiB if ($Params.AutoSize) { $totalPhysicalMemory = (Get-CimInstance -ClassName Win32_PhysicalMemory -ErrorAction Stop | Measure-Object -Property Capacity -Sum).Sum [uint32]$InitSize = if ($totalPhysicalMemory -ge 768GB) { 131072 } elseif ($totalPhysicalMemory -le 128GB) { 32768 } else { 65536 } [uint32]$MaxSize = $InitSize } else { [uint32]$InitSize = $Params.InitialSizeMB [uint32]$MaxSize = $Params.MaximumSizeMB } # Get auto-managed state BEFORE the "already configured" check $PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).AutomaticManagedPagefile # Check if current settings already match the target $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if (-not $PageFileAutoManaged -and $pfConfig -and ($pfConfig | Measure-Object).Count -eq 1 -and $pfConfig.Name -eq "${DriveLetter}pagefile.sys" -and $pfConfig.InitialSize -eq $InitSize -and $pfConfig.MaximumSize -eq $MaxSize) { $result.Success = $true $result.Action = 'NoChangeRequired' $result.PageFileSizeMB = $MaxSize return $result } # Backup current settings (PageFileAutoManaged already queried above) $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' if (-not (Test-Path $BackupDir)) { New-Item $BackupDir -ItemType Directory -Force | Out-Null } $DateFormatted = Get-Date -f 'yyyyMMdd-HHmm' $BackupFile = "$BackupDir\PageFile_Settings_$DateFormatted.json" $pfConfig = $null if (-not $PageFileAutoManaged) { $pfSetting = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfSetting) { $pfConfig = @{ Name = $pfSetting.Name InitialSize = $pfSetting.InitialSize MaximumSize = $pfSetting.MaximumSize Caption = $pfSetting.Caption Description = $pfSetting.Description SettingID = $pfSetting.SettingID } } } $pfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue $backupObject = [PSCustomObject]@{ BackupDate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Node = $env:COMPUTERNAME PageFileAutoManaged = $PageFileAutoManaged PageFileConfig = $pfConfig PageFileUsage = @{ AllocatedBaseSize = if ($pfUsage) { $pfUsage.AllocatedBaseSize } else { $null } CurrentUsage = if ($pfUsage) { $pfUsage.CurrentUsage } else { $null } PeakUsage = if ($pfUsage) { $pfUsage.PeakUsage } else { $null } Name = if ($pfUsage) { $pfUsage.Name } else { $null } } } [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false))) # Check disk space: page file needs InitSize MB on the drive $Disk = Get-PSDrive -ErrorAction Stop | Where-Object { $_.Root -eq $DriveLetter } if (-not $Disk) { throw "Drive '$DriveLetter' not found on $env:COMPUTERNAME." } $currentPfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "$($DriveLetter.TrimEnd('\'))\*" } $currentPfSizeMB = if ($currentPfUsage) { $currentPfUsage.AllocatedBaseSize } else { 0 } # Net new space = target size - current page file size (if on same drive) $netNewMB = [math]::Max(0, $MaxSize - $currentPfSizeMB) $freeGiB = [math]::Round($Disk.Free / 1GB, 2) $neededGiB = [math]::Round($netNewMB / 1024 + 10, 1) # +10 GiB OS margin if ($freeGiB -lt $neededGiB) { throw "Insufficient disk space on $DriveLetter for ${MaxSize} MB page file. Free: ${freeGiB} GiB, Needed: ${neededGiB} GiB." } # Disable auto-management if enabled if ($PageFileAutoManaged) { $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } -ErrorAction Stop } # Remove existing page file config $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfConfig) { $pfConfig | Remove-CimInstance -ErrorAction Stop } # Create new page file $newPf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${DriveLetter}pagefile.sys" } -ErrorAction Stop $newPf | Set-CimInstance -Property @{ InitialSize = $InitSize; MaximumSize = $MaxSize } -ErrorAction Stop $result.PageFileSizeMB = $MaxSize $result.Success = $true } catch { $result.Error = $_.Exception.Message } $result } -ArgumentList (,$remoteArgs) -ErrorAction SilentlyContinue -ErrorVariable remoteErrors if ($remoteErrors) { foreach ($err in $remoteErrors) { Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose } } $successCount = 0; $failCount = 0; $noChangeCount = 0 foreach ($nodeResult in $remoteResults) { if ($nodeResult.Success -and $nodeResult.Action -eq 'NoChangeRequired') { Write-Verbose " $($nodeResult.Node): Already configured ($($nodeResult.PageFileSizeMB) MB) — no changes needed" -Verbose $noChangeCount++; $successCount++ } elseif ($nodeResult.Success) { Write-Verbose " $($nodeResult.Node): SUCCESS ($($nodeResult.PageFileSizeMB) MB)" -Verbose; $successCount++ } else { Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)" -Verbose; $failCount++ } } Write-Verbose "Cluster operation complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose if ($failCount -gt 0) { Write-Warning "$failCount node(s) failed. Review the output above for details." } return $remoteResults } # --------------------------------------------------------------- # Node scope: set manual page file on local node # --------------------------------------------------------------- # Normalize drive letter if ($PageFileFileDriveLetter.Length -eq 2) { $PageFileFileDriveLetter = "$PageFileFileDriveLetter\" } # Check if current settings already match the target $dumpDrive = $PageFileFileDriveLetter.TrimEnd('\') [bool]$PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).AutomaticManagedPagefile $currentPfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue $currentPfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "${dumpDrive}\*" } $currentPfSizeMB = if ($currentPfUsage) { $currentPfUsage.AllocatedBaseSize } else { 0 } if (-not $PageFileAutoManaged -and $currentPfConfig -and ($currentPfConfig | Measure-Object).Count -eq 1 -and $currentPfConfig.Name -eq "${PageFileFileDriveLetter}pagefile.sys" -and $currentPfConfig.InitialSize -eq $InitialSizeMB -and $currentPfConfig.MaximumSize -eq $MaximumSizeMB) { Write-Verbose "Page file is already configured as requested: ${PageFileFileDriveLetter}pagefile.sys (Initial: ${InitialSizeMB} MB, Max: ${MaximumSizeMB} MB). No changes needed." -Verbose return [PSCustomObject]@{ Node = $env:COMPUTERNAME Success = $true Action = 'NoChangeRequired' InitialSizeMB = $InitialSizeMB MaximumSizeMB = $MaximumSizeMB PreviousAutoManaged = $false Error = $null } } # Check disk space $Disk = Get-PSDrive -ErrorAction Stop | Where-Object { $_.Root -eq $PageFileFileDriveLetter } if (-not $Disk) { throw "Drive '$PageFileFileDriveLetter' not found." } $netNewMB = [math]::Max(0, $MaximumSizeMB - $currentPfSizeMB) $freeGiB = [math]::Round($Disk.Free / 1GB, 2) $neededGiB = [math]::Round($netNewMB / 1024 + 10, 1) if ($freeGiB -lt $neededGiB) { throw "Insufficient disk space on '$PageFileFileDriveLetter' for ${MaximumSizeMB} MB page file. Free: ${freeGiB} GiB, Needed: ~${neededGiB} GiB (${netNewMB} MB net new + 10 GiB OS margin). Current page file on this drive: ${currentPfSizeMB} MB." } # ShouldProcess gate $actionDescription = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { "Set kernel-dump-recommended manual page file (${InitialSizeMB} MB / ${targetSizeGiB} GiB) on ${PageFileFileDriveLetter}" } else { "Set manual page file (Initial: ${InitialSizeMB} MB, Max: ${MaximumSizeMB} MB) on ${PageFileFileDriveLetter}" } if (-not $PSCmdlet.ShouldProcess("Page file on $PageFileFileDriveLetter", $actionDescription)) { return } # Disable auto-management if enabled (already queried above) if ($PageFileAutoManaged) { Write-Verbose "Disabling automatic page file management..." -Verbose $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } -ErrorAction Stop Write-Verbose "Automatic page file management disabled." -Verbose } # Remove existing page file config $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfConfig) { if (($pfConfig | Measure-Object).Count -eq 1) { $pfConfig | Remove-CimInstance -ErrorAction Stop Write-Verbose "Existing page file settings removed." -Verbose } else { throw "Unexpected: more than one page file configured (found $(($pfConfig | Measure-Object).Count)). Remove extra page files manually first." } } # Create new page file with specified size try { $PageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${PageFileFileDriveLetter}pagefile.sys" } -ErrorAction Stop $PageFile | Set-CimInstance -Property @{ InitialSize = $InitialSizeMB; MaximumSize = $MaximumSizeMB } -ErrorAction Stop } catch { throw "Failed to create page file with InitialSize=${InitialSizeMB} MB, MaximumSize=${MaximumSizeMB} MB. Error: $($_.Exception.Message)" } if (-not $PageFile) { throw "Failed to configure page file." } Write-Verbose "Page file configured: ${PageFileFileDriveLetter}pagefile.sys (InitialSize: ${InitialSizeMB} MB, MaximumSize: ${MaximumSizeMB} MB / ${targetSizeGiB} GiB)" -Verbose Write-Verbose "Automatic page file management has been disabled." -Verbose Write-Verbose "A system restart is required for page file changes to take effect." -Verbose [PSCustomObject]@{ Node = $env:COMPUTERNAME Success = $true Action = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { 'SetPageFileKernelDumpRecommended' } else { 'SetPageFileManual' } InitialSizeMB = $InitialSizeMB MaximumSizeMB = $MaximumSizeMB PreviousAutoManaged = $PageFileAutoManaged Error = $null } } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Set-AzStackHciPageFileSettings function" } } # End of Set-AzStackHciPageFileSettings # /////////////////////////////////////////////////////////////////// # Set-AzStackHciPageFileSettingsMinimal Function # Used to set a static 4GB page file on the system, target drive is # the only parameter. # /////////////////////////////////////////////////////////////////// Function Set-AzStackHciPageFileSettingsMinimal { <# .SYNOPSIS Sets page file settings to minimum 4GB fixed size .DESCRIPTION Queries the system for current page file settings and sets a fixed 4GB page file. Use -Scope Cluster to apply settings to all nodes in the failover cluster via Invoke-Command. The scriptblock is self-contained and does not require the module on remote nodes. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([PSCustomObject[]])] param ( # Path to drive letter for PageFile [Parameter(Mandatory=$true,Position=1)] [ValidateScript({Test-Path $_})] [String] [ValidatePattern('^[C-Zc-z]:\\?$')]$PageFileFileDriveLetter, # Drive letter for the page file, must be a valid drive letter (C: through Z:) # Scope: 'Node' (default) for local node only, 'Cluster' to apply to all cluster nodes [Parameter(Mandatory=$false)] [ValidateSet('Node','Cluster')] [string]$Scope = 'Node', [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")] [switch]$NoOutput ) begin { # Requires administrator permissions to set page file settings if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } # Call function to get current Page File settings and save to backup log file (skip in Cluster mode — each node backs up inline) if ($Scope -ne 'Cluster') { $null = Get-AzStackHciPageFileSettings } } process { # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active $script:SilentMode = $false # Handle -NoOutput: suppress all console output if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # --------------------------------------------------------------- # Cluster scope: fan out to all cluster nodes via Invoke-Command # --------------------------------------------------------------- if ($Scope -eq 'Cluster') { if (-not (Test-CommandExists Get-Cluster)) { throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node." } try { [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop } catch { throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)" } Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose if (-not $PSCmdlet.ShouldProcess("Cluster '$ClusterName' ($($Nodes -join ', '))", "Set fixed 4GB page file on $($Nodes.Count) cluster nodes")) { return } $remoteDriveLetter = $PageFileFileDriveLetter # Self-contained scriptblock — does NOT depend on the module being installed on remote nodes $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock { param($DriveLetter) $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Error = $null } try { # Normalize drive letter if ($DriveLetter.Length -eq 2) { $DriveLetter = "$DriveLetter\" } # ── Inline backup of current page file settings ── $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' if (-not (Test-Path $BackupDir)) { New-Item $BackupDir -ItemType Directory -Force | Out-Null } $DateFormatted = Get-Date -f 'yyyyMMdd-HHmm' $BackupFile = "$BackupDir\PageFile_Settings_$DateFormatted.json" $PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).AutomaticManagedPagefile $pfConfigBackup = $null if (-not $PageFileAutoManaged) { $pfSetting = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfSetting) { $pfConfigBackup = @{ Name = $pfSetting.Name InitialSize = $pfSetting.InitialSize MaximumSize = $pfSetting.MaximumSize Caption = $pfSetting.Caption Description = $pfSetting.Description SettingID = $pfSetting.SettingID } } } $pfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue $backupObject = [PSCustomObject]@{ BackupDate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Node = $env:COMPUTERNAME PageFileAutoManaged = $PageFileAutoManaged PageFileConfig = $pfConfigBackup PageFileUsage = @{ AllocatedBaseSize = if ($pfUsage) { $pfUsage.AllocatedBaseSize } else { $null } CurrentUsage = if ($pfUsage) { $pfUsage.CurrentUsage } else { $null } PeakUsage = if ($pfUsage) { $pfUsage.PeakUsage } else { $null } Name = if ($pfUsage) { $pfUsage.Name } else { $null } } } [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false))) # ── Set 4GB fixed page file ── if (-not $PageFileAutoManaged) { # Manual: remove existing page file config, create 4GB fixed $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfConfig) { if (($pfConfig | Measure-Object).Count -eq 1) { $pfConfig | Remove-CimInstance -ErrorAction Stop } else { throw "Unexpected: more than one page file configured (found $(($pfConfig | Measure-Object).Count))" } } $newPf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${DriveLetter}pagefile.sys" } -ErrorAction Stop $newPf | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } -ErrorAction Stop } else { # Auto-managed: disable auto management, then create 4GB fixed $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } -ErrorAction Stop $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($pfConfig) { if (($pfConfig | Measure-Object).Count -eq 1) { $pfConfig | Remove-CimInstance -ErrorAction Stop } else { throw "Unexpected: more than one page file configured (found $(($pfConfig | Measure-Object).Count))" } } $newPf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${DriveLetter}pagefile.sys" } -ErrorAction Stop $newPf | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } -ErrorAction Stop } if (-not $newPf) { throw 'Failed to configure page file' } $result.Success = $true } catch { $result.Error = $_.Exception.Message } $result } -ArgumentList $remoteDriveLetter -ErrorAction SilentlyContinue -ErrorVariable remoteErrors # Report per-node results if ($remoteErrors) { foreach ($err in $remoteErrors) { Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose } } $successCount = 0 $failCount = 0 foreach ($nodeResult in $remoteResults) { if ($nodeResult.Success) { Write-Verbose " $($nodeResult.Node): SUCCESS" -Verbose $successCount++ } else { Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)" -Verbose $failCount++ } } Write-Verbose "Cluster operation complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose if ($failCount -gt 0) { Write-Warning "$failCount node(s) failed. Review the output above for details." } return $remoteResults } # if the drive letter is only 2 characters (C:), add a backslash to the end (C:\) if($PageFileFileDriveLetter.Length -eq 2){ $PageFileFileDriveLetter = $($PageFileFileDriveLetter + "\") } # ShouldProcess gate: prompt user before modifying page file settings if (-not $PSCmdlet.ShouldProcess("Page file on $PageFileFileDriveLetter", "Set fixed 4GB page file")) { return } try { # Get current page file settings [bool]$script:PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile } catch { Write-Verbose "Failed to get current page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Failed to get current page file settings. Error: $($_.Exception.Message)" } # If page file is NOT automatically managed if($script:PageFileAutoManaged -eq $False){ # Page File manual settings Write-HostAzS "Automatic management of page file already disabled" Write-HostAzS "Configuring a fixed page file of 4GB size using file: $($PageFileFileDriveLetter)pagefile.sys`n" try { $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting } catch { Write-Verbose "Failed to get existing page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to get existing page file settings. Error: $($_.Exception.Message)" } # Remove existing manual page file settings if($PageFileConfiguration){ if(($PageFileConfiguration | Measure-Object).Count -eq 1){ try { # Delete existing page file settings $PageFileConfiguration | Remove-CimInstance } catch { Write-Verbose "Failed to delete existing page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to delete existing page file settings. Error: $($_.Exception.Message)" } Write-Verbose "Existing page file settings removed." -Verbose } else { Write-Verbose "Unexpected configuration, more than one page file is currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." -Verbose Throw "Error: Unexpected configuration, more than one page file currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." } } else { Write-Verbose "Existing page file settings not found." -Verbose } # Set a New page file, and set the size to 4GB fixed try { # Use New-CimInstance to create a new page file setting, due to "Set-WmiInstance : Generic failure" error, if no existing page file settings are configured (as deleted). $PageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name= "$($PageFileFileDriveLetter)pagefile.sys" } $PageFile | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } } catch { Write-Verbose "Failed to set minimum page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Failed to set minimum page file settings. Error: $($_.Exception.Message)" } if($PageFile){ Write-Verbose "Page File settings updated successfully to a fixed size of 4GB" -Verbose Write-Verbose "Page File: = '$($pagefile.Description)'" -Verbose } else { Write-Verbose "Failed to configure minimum page file settings." -Verbose Throw "Error: Failed to configure minimum page file settings." } [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'SetMinimumPageFile'; PreviousAutoManaged = $false; Error = $null } } elseif($script:PageFileAutoManaged -eq $True){ # If the page file is currently automatically managed Write-HostAzS "Disabling automatic management of page file" # Remove System Managed: try { $ComputerSystem = Get-CimInstance -ClassName Win32_ComputerSystem } catch { Write-Verbose "Failed to get existing automatic management settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to get existing automatic management settings. Error: $($_.Exception.Message)" } try { $ComputerSystem | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } } catch { Write-Verbose "Failed to disable automatic management of page file. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to disable automatic management of page file. Error: $($_.Exception.Message)" } Write-Verbose "Automatic management of page file disabled" -Verbose try { $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting } catch { Write-Verbose "Failed to get existing page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to get existing page file settings. Error: $($_.Exception.Message)" } Write-HostAzS "Configuring a fixed page file of 4GB size using file: $($PageFileFileDriveLetter)pagefile.sys`n" # Remove existing page file setting and set a new page file, and set the size to 4GB fixed if($PageFileConfiguration){ if(($PageFileConfiguration | Measure-Object).Count -eq 1){ try { # Delete existing page file settings $PageFileConfiguration | Remove-CimInstance } catch { Write-Verbose "Failed to delete existing page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Error: Failed to delete existing page file settings. Error: $($_.Exception.Message)" } Write-Verbose "Existing page file settings removed." -Verbose } else { Write-Verbose "Unexpected configuration, more than one page file is currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." -Verbose Throw "Error: Unexpected configuration, more than one page file currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." } # Set a New page file, and set the size to 4GB fixed try { # Use New-CimInstance to create a new page file setting, due to "Set-WmiInstance : Generic failure" error, if no existing page file settings are configured (as deleted). $PageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name= "$($PageFileFileDriveLetter)pagefile.sys" } $PageFile | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } } catch { Write-Verbose "Failed to set new page file settings. Error: $($_.Exception.Message)" -Verbose Throw "Failed to set new page file settings. Error: $($_.Exception.Message)" } if($PageFile){ Write-Verbose "Page File settings updated successfully to a fixed size of 4GB" -Verbose Write-Verbose "Page File: = '$($pagefile.Description)'" -Verbose } else { Write-Verbose "Failed to configure minimum page file settings." -Verbose Throw "Error: Failed to configure minimum page file settings." } Write-Verbose "Page File settings updated to minimum recommended configuration, (static size of 4GB)." -Verbose } # End of If PageFileConfiguration Write-Verbose "A system restart is required for Page File changes to take effect.`n`n" -Verbose [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'SetMinimumPageFile'; PreviousAutoManaged = $true; Error = $null } } else { Write-Error "Current Settings: Page File automatic management is Unknown (not enabled or disabled)." -Verbose Throw "Error: Current Settings: Page File automatic management is Unknown (not enabled or disabled)." } # End of If PageFileAutoManaged } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Set-AzStackHciPageFileSettingsMinimal function" } } # End of Set-AzStackHciPageFileSettingsMinimal # /////////////////////////////////////////////////////////////////// # Restore-AzStackHciPageFileSettings function # Used to restore or roll back page file settings # /////////////////////////////////////////////////////////////////// Function Restore-AzStackHciPageFileSettings { <# .SYNOPSIS Restores page file settings from a backup file .DESCRIPTION Restores the page file settings from a backup file, using the 'PageFile_Settings_YYYYMMDD.json' file. Requires administrator permissions to restore page file settings. Use -Scope Cluster to restore settings on all nodes in the failover cluster via Invoke-Command. In Cluster mode, each node auto-discovers its most recent backup file if no path is specified. The scriptblock is self-contained and does not require the module on remote nodes. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([PSCustomObject[]])] param ( # File path to backup file (required for Node scope; optional for Cluster scope where auto-discovery is used) [Parameter(Mandatory=$false,Position=0)] [ValidateScript({Test-Path $_})] [String]$PageFileSettingsFilePath, # File used to restore settings from, must be a valid path # Scope: 'Node' (default) for local node only, 'Cluster' to restore on all cluster nodes [Parameter(Mandatory=$false)] [ValidateSet('Node','Cluster')] [string]$Scope = 'Node', [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")] [switch]$NoOutput ) begin { # Requires administrator permissions to set page file settings if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } } process { # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active $script:SilentMode = $false # Handle -NoOutput: suppress all console output if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # --------------------------------------------------------------- # Cluster scope: fan out to all cluster nodes via Invoke-Command # --------------------------------------------------------------- if ($Scope -eq 'Cluster') { if (-not (Test-CommandExists Get-Cluster)) { throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node." } try { [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop } catch { throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)" } Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose if (-not $PSCmdlet.ShouldProcess("Cluster '$ClusterName' ($($Nodes -join ', '))", "Restore page file settings from backup on $($Nodes.Count) cluster nodes")) { return } # Pass the file path if provided; otherwise each node auto-discovers its latest backup $remoteFilePath = if ($PSBoundParameters.ContainsKey('PageFileSettingsFilePath')) { $PageFileSettingsFilePath } else { $null } # Self-contained scriptblock — does NOT depend on the module being installed on remote nodes $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock { param($FilePath) $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Action = $null; BackupFile = $null; Error = $null } try { $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' # Auto-discover the most recent backup file if no path was provided if ([string]::IsNullOrEmpty($FilePath)) { $LatestFile = Get-ChildItem "$BackupDir\PageFile_Settings_*.json" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if (-not $LatestFile) { throw "WARNING: No PageFile backup files found in '$BackupDir' on $env:COMPUTERNAME. Run Get-AzStackHciPageFileSettings first." } $FilePath = $LatestFile.FullName } $result.BackupFile = $FilePath # Validate file path if ($FilePath -notlike 'C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_*') { throw "Backup file path must match 'C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_*'. Got: '$FilePath'" } if (-not (Test-Path $FilePath)) { throw "Backup file not found: '$FilePath'" } $backup = Get-Content $FilePath -Raw -ErrorAction Stop | ConvertFrom-Json $wasAutoManaged = $backup.PageFileAutoManaged $currentPageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile if ($wasAutoManaged) { # Backup had auto-managed: restore to auto-managed $nameValue = $backup.PageFileUsage.Name if (-not $nameValue) { throw 'Could not find page file Name in backup file' } if ($currentPageFileAutoManaged) { throw 'Page File is already automatically managed, no action required.' } # Remove current manual page file $currentPf = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($currentPf -and ($currentPf | Measure-Object).Count -eq 1) { $currentPf | Remove-CimInstance -ErrorAction Stop } elseif ($currentPf -and ($currentPf | Measure-Object).Count -gt 1) { throw "Unexpected: $(($currentPf | Measure-Object).Count) page files configured" } # Create with size 0 (system-managed) $pf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $nameValue } $pf | Set-CimInstance -Property @{ InitialSize = 0; MaximumSize = 0 } # Enable automatic management $cs = Get-CimInstance -ClassName Win32_ComputerSystem $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $true } } else { # Backup had manual config: restore with backup's settings $restoreName = $backup.PageFileConfig.Name $restoreInitial = [int]$backup.PageFileConfig.InitialSize $restoreMax = [int]$backup.PageFileConfig.MaximumSize if (-not $restoreName -or $null -eq $restoreInitial -or $null -eq $restoreMax) { throw 'Could not parse page file Name, InitialSize, or MaximumSize from backup file' } # Remove current config $currentPf = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue if ($currentPf -and ($currentPf | Measure-Object).Count -eq 1) { $currentPf | Remove-CimInstance -ErrorAction Stop } elseif ($currentPf -and ($currentPf | Measure-Object).Count -gt 1) { throw "Unexpected: $(($currentPf | Measure-Object).Count) page files configured" } # Restore with backup settings $pf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $restoreName } $pf | Set-CimInstance -Property @{ InitialSize = $restoreInitial; MaximumSize = $restoreMax } } $result.Action = if ($wasAutoManaged) { 'RestoreAutoManaged' } else { 'RestoreManual' } $result.Success = $true } catch { $result.Error = $_.Exception.Message } $result } -ArgumentList $remoteFilePath -ErrorAction SilentlyContinue -ErrorVariable remoteErrors # Report per-node results if ($remoteErrors) { foreach ($err in $remoteErrors) { Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose } } $successCount = 0 $failCount = 0 foreach ($nodeResult in $remoteResults) { $backupInfo = if ($nodeResult.BackupFile) { " (from $($nodeResult.BackupFile))" } else { '' } if ($nodeResult.Success) { Write-Verbose " $($nodeResult.Node): SUCCESS$backupInfo" -Verbose $successCount++ } else { Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)$backupInfo" -Verbose $failCount++ } } Write-Verbose "Cluster restore complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose if ($failCount -gt 0) { Write-Warning "$failCount node(s) failed. Review the output above for details." } return $remoteResults } # --------------------------------------------------------------- # Node scope: auto-discover latest backup if no path provided # --------------------------------------------------------------- if ([string]::IsNullOrEmpty($PageFileSettingsFilePath)) { $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings' $LatestFile = Get-ChildItem "$BackupDir\PageFile_Settings_*.json" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if (-not $LatestFile) { Write-Warning "No PageFile backup files found in '$BackupDir' on $env:COMPUTERNAME." throw "No PageFile backup files found in '$BackupDir'. Run Get-AzStackHciPageFileSettings first to create a backup." } $PageFileSettingsFilePath = $LatestFile.FullName Write-Verbose "Auto-discovered latest backup file: $PageFileSettingsFilePath (Last modified: $($LatestFile.LastWriteTime))" -Verbose } # Validate backup file path if ($PageFileSettingsFilePath -notlike "C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_*") { throw "Error: Page File settings backup file path must start with 'C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_'" } # Read and parse the JSON backup file Write-Verbose "Reading Page File settings from backup file: $PageFileSettingsFilePath" -Verbose $backup = Get-Content $PageFileSettingsFilePath -Raw -ErrorAction Stop | ConvertFrom-Json try { # Get current page file settings [bool]$script:PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile } catch { throw "Failed to get current page file settings. Error: $($_.Exception.Message)" } # ShouldProcess confirmation before restoring if (-not $PSCmdlet.ShouldProcess("Page file settings on $env:COMPUTERNAME", "Restore page file settings from '$PageFileSettingsFilePath' (saved on $($backup.BackupDate))")) { return } [bool]$RestorePageFileAutoManaged = $backup.PageFileAutoManaged if ($RestorePageFileAutoManaged) { # Backup had auto-managed: restore to auto-managed $nameValue = $backup.PageFileUsage.Name if (-not $nameValue) { throw 'Could not find page file Name in backup file' } if ($script:PageFileAutoManaged) { throw "Restore Page File settings: Page File is already automatically managed, no action required." } try { # Remove existing manual page file $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting if (($script:PageFileConfiguration | Measure-Object).Count -eq 1) { $script:PageFileConfiguration | Remove-CimInstance -ErrorAction Stop Write-Verbose "Existing manual page file settings removed." -Verbose } elseif (($script:PageFileConfiguration | Measure-Object).Count -gt 1) { throw "Unexpected: $(($script:PageFileConfiguration | Measure-Object).Count) page files configured" } # Create system-managed page file $RestorePageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $nameValue } -ErrorAction Stop $RestorePageFile | Set-CimInstance -Property @{ InitialSize = 0; MaximumSize = 0 } -ErrorAction Stop # Enable automatic management $ComputerSystem = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $ComputerSystem | Set-CimInstance -Property @{ AutomaticManagedPagefile = $true } -ErrorAction Stop } catch { throw "Failed to restore auto-managed page file settings. Error: $($_.Exception.Message)" } Write-Verbose "Page File settings reverted to automatically managed." -Verbose Write-Verbose "A system restart is required for the Page File changes to take effect." -Verbose return [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'RestoreAutoManaged'; BackupFile = $PageFileSettingsFilePath; Error = $null } } else { # Backup had manual config: restore with backup's settings $restoreName = $backup.PageFileConfig.Name $restoreInitial = [int]$backup.PageFileConfig.InitialSize $restoreMax = [int]$backup.PageFileConfig.MaximumSize if (-not $restoreName -or $null -eq $restoreInitial -or $null -eq $restoreMax) { throw 'Could not parse page file Name, InitialSize, or MaximumSize from backup file' } try { # Remove existing page file $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting if (($script:PageFileConfiguration | Measure-Object).Count -eq 1) { $script:PageFileConfiguration | Remove-CimInstance -ErrorAction Stop Write-Verbose "Existing page file settings removed." -Verbose } elseif (($script:PageFileConfiguration | Measure-Object).Count -gt 1) { throw "Unexpected: $(($script:PageFileConfiguration | Measure-Object).Count) page files configured" } # Restore with backup settings $RestorePageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $restoreName } -ErrorAction Stop $RestorePageFile | Set-CimInstance -Property @{ InitialSize = $restoreInitial; MaximumSize = $restoreMax } -ErrorAction Stop } catch { throw "Failed to restore manual page file settings. Error: $($_.Exception.Message)" } Write-Verbose "Page File configured with InitialSize = $restoreInitial and MaximumSize = $restoreMax" -Verbose Write-Verbose "A system restart is required for the Page File changes to take effect." -Verbose return [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'RestoreManual'; BackupFile = $PageFileSettingsFilePath; Error = $null } } } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Restore-AzStackHciPageFileSettings function" } } # End of Restore-AzStackHciPageFileSettings function # SIG # Begin signature block # MIInRQYJKoZIhvcNAQcCoIInNjCCJzICAQExDzANBglghkgBZQMEAgEFADB5Bgor # 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 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghnhMIIZ3QIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIEQ9ghRW # oS1bqbKpK7STbJcqqAIptC9CGXc7YxHBfR/xMEIGCisGAQQBgjcCAQwxNDAyoBSA # EgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20w # DQYJKoZIhvcNAQEBBQAEggEAMenmWO+Lp8lVNCtStdQllqBdcQwlauP4Ba5VRww2 # T64ASUArMbvETmyM2zjYBGRjMdPsOqSic0PZihTXnk3lW6Eo4le4qQWUrK6/+O0R # VLdicfqLCF/kCzEwzR1h7v89q8DKH0cGco+sQK9i5P/CshTKAaXoTSqC1uJ3RIJH # SAg7oea7t2BavFm10v6gMk4aoeSoncxaMgJ4ySUK3UAkfg+TCz9RjXRBp5JkhRT0 # o9bld0WILJ08fO+kMkTXCkcxq1nCKLJmBxOseqgchCy/acvXBv9QtAZ2vpfLjfWA # h00b6lnUzye3K/M3gqpc2Mh42BvCpNk7Wj+k3SSCF76P56GCF5MwghePBgorBgEE # AYI3AwMBMYIXfzCCF3sGCSqGSIb3DQEHAqCCF2wwghdoAgEDMQ8wDQYJYIZIAWUD # BAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGEWQoD # ATAxMA0GCWCGSAFlAwQCAQUABCDsWQE+n33tTuUgNi5DnCRcdtzEThHpwoNUaPuZ # 0+lRLAIGajFoxCHeGBIyMDI2MDcxNjIyNTUzNC43M1owBIACAfSggdGkgc4wgcsx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p # Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNT # IEVTTjpEQzAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgU2VydmljZaCCEeowggcgMIIFCKADAgECAhMzAAACJDuEIbAsrGQiAAEAAAIk # MA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4X # DTI2MDIxOTE5Mzk1OVoXDTI3MDUxNzE5Mzk1OVowgcsxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNh # IE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjpEQzAwLTA1RTAt # RDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIw # DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKPpbdRpDZmviE29LLuPtQw8VXKz # toTEYH4kXDKTPNeDeNrJib2A4tcnu02FTZ6aGstAI5lyAu/PoWSqaCHNDHOaSAq0 # tiIpoTOGiA79x7SVOF0s11W0zBA5iCj5e1cBlxWIFfgtweTfxG6xmIXvDFJrm38v # GJzTj5n+GXLWAlCkh4UOqnhr0+4u3yux8fTm9b2lT26uIZ0PF8lef+Vzj0LFteoD # cRfXsvbhtzq36YW48MAkoqlqLddeoXacmWlM992sDb2xZNI0qKD0K0ELm3NCPR+V # uxr/jCo7275GS7CllvdvuqdbkV0WsNHW9CZd+OXJQ/1k7fzzf03BK6Ie2+wUI2RM # 0hfw4vldWrWewrK7/8Z4hn1i7Gx8sF52obTbg8MRHKsCzSm99RY4tqlVBqMc+gKe # 41Iq9sSHuzkhDRiC6kaOL4fusgPHb+YgQj7pDxbAG2TdjHKGOPQZfD3T2LQSRORX # LL7XIAOPBILxvDaozj4xziHLK2VnNJzQg9QGrVgadjAKMjBrn+UxbSkWf8ekl0Hp # d4y5O1hM6lo+ijrgWNCvItdaN3ii+nDmU7Dtf6/cT2TA31UEL7AkRIEQILWBkwJL # lNpXB8TXDimdddvWpP1uOBGw+Dh2SWu5RN2if/dI23RrRDk1zZSX6syVDFeg/2Kx # fAw2co7kkmSpENFVAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUcx+RfW7/MksIx7SC # piK3HW0Ad6gwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0f # BFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwv # TWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsG # AQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAx # MCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO # BgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAD7AdJuaEikzwJFVni2T # rbiFD4t1lcTiqh5C6LvsJ41reOrUU7OLsxEqSSjp2IQMdc81a8BqDFqy0J7A/Obl # MI2HWzioIeHhHYb+vjzBT8ylzrz9YOYnLkIhCf8XCmzWxs1QS7sHODTTipQshUn3 # reOj9qbjHAqDCH69JUvv92Gx9Pt2+GlF11tgtBMdmDC40HpCFwQSyCiAtXA1GPft # URZkOLCgx3HILthitC7owJW2LMec62RJfsWoiiLqOVx+p+jrX24Mf2vyTaoA4cJ4 # QCopcrKYhcMxwYaUR0MVtiINmA8IEzQgeAB6KVRKifTvCMe7R7SywGa0Fp89vgZ3 # 7kW5GdYbdcZ73U0KksqqYVr/gaRXP04zNlSDyhzPEL/glPcd/jkkS2zNOhfA2yRX # ck0Jy7Ygi2vpIkeaLcQNUAMNFI2F3MVGliamUYSU+XkZGg+0mIMS9Ehu/kwUojDb # H2Cd6F/ki8GMLhmQGD7gZOmoYTeaafMXech6Q6Rfi6DT/SY3YJJquG5KL02Ycg6l # Q3Z5AdS2BNv/4aaruCS0IzAir8k4JgiJNiqm/WhuMAYp1Yw8KuVLI0CzSNljOSFr # nfnXnw0zH7AEa+x8WhWwIwbk5ynq9boJfK5ZFtRWoxTU6tBsd93LMmluEkLU9sBk # jIkJs35UGANMDNMpjzDghJLBMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAA # AAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUg # QXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8 # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N # aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQAD # ggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2 # AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpS # g0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2r # rPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k # 45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSu # eik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09 # /SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR # 6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxC # aC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaD # IV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMUR # HXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMB # AAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQq # p1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ # 6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0 # cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRt # MBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBB # MAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP # 6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWlj # cm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2 # LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMu # Y3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2 # Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03d # mLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVtI1Tk # eFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kp # icO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKp # W99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrY # UP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QB # jloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkB # RH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0V # iY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq # 0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1V # M1izoXBm8qGCA00wggI1AgEBMIH5oYHRpIHOMIHLMQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBP # cGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046REMwMC0wNUUwLUQ5 # NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAH # BgUrDgMCGgMVAKYI8duax4BJ97/9sa1f15Ab7T7joIGDMIGApH4wfDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQELBQACBQDuA2/8MCIYDzIw # MjYwNzE2MTQ1ODA0WhgPMjAyNjA3MTcxNDU4MDRaMHQwOgYKKwYBBAGEWQoEATEs # MCowCgIFAO4Db/wCAQAwBwIBAAICILwwBwIBAAICEhIwCgIFAO4EwXwCAQAwNgYK # KwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQAC # AwGGoDANBgkqhkiG9w0BAQsFAAOCAQEASo4tiOsf1C64JvhK+SDvM/6U2X620c/z # yxVeYQaSrry2yOyg3TOIbemA50a1WbxUKlXGr7tRG1Ud+bS/K8mDN9PmJRUjFCz1 # xkqYO8RuN3BCayD/GtUkQqgDv9yLMtMd9RtEKSYGf8Sbrcrsixe9OEM8IXfBfgr4 # jpOdtZox4CzkYpVRtCL7Tyy99maFG6b7Io7SHd973bSkh5qrdLUDD5H0+dEo5EPD # 4SVdAQF980NkaCMbi2vjVp9j3S3d3EAoX8ah90LNeSLAAt+2mJwSGW2mrs7b6iV1 # bmO485VxEoTFWO7beRj5RnZ6BLH8zvMAkzRqsj+trEYZACdcXonyGTGCBA0wggQJ # AgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAk # BgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAACJDuEIbAs # rGQiAAEAAAIkMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZI # hvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEINjs7oTq1pAyf9PU19/fyh46AoOwPa6I # /ESDNRRwgHtYMIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgSCE9N2qb91HJ # nQFzNdx2WhUSogJ1yalU1sf0IRXNZI4wgZgwgYCkfjB8MQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T # dGFtcCBQQ0EgMjAxMAITMwAAAiQ7hCGwLKxkIgABAAACJDAiBCBxDDB0yrsug0I7 # XSi4bLYtacu+zfwcUuU087R2B30wuTANBgkqhkiG9w0BAQsFAASCAgBb4gWXvNxE # LlPhxEK3Ms38JLhVJYJCTL5U4/3a3R5IYr3xO/IU/HSIvHXujewrVVaHdhUVeMJp # negSi6wOWcETYFDBsckBYkHIXUQAmPFlceeLwN+5MD5TlplgxQJIlQCyMHdQ6yv0 # 9QrWMzMNpU7y7ONLSWmr61tLQQaweFmsTHQz9bMcMXZ7jeazFoRFBaPozFwwuixC # u7HY2a+64quEn0GBS0WqeW6oIGCWElONjpa7I+fxH6w3KvFXSqOqDYJoBE8H+wQH # sV5RSoW5iitdEbeesbSfGJlOHaWIKHhanzhsH00xmilmdiSCOGmozw+g5Es+1Hjo # zYCD4cDyc8rsy9kFTbagjwTYK1sxQLoqoMEUsn9K0j+LdvtWPDBAg6zuM8Mva0Bi # kzIs1LoV9znJwVsRfmTHZKHrjIu7dTudn1iaoy3v1DjtfwR+SXJF1UUA2oIxOkC4 # nqSaoOz6p+/1ESiKdv2B2X8gj2YKY56o3R1tUTQuPhARSFfbXkp6y3g5PnL0/zV1 # Ls8CZ4ZEbmnyc5GhvRRszfSLHYEAlKAmZdM3BC5mNUDA4PUIXs2MeYDDKc64HePF # VpQO+09rBgZTo+yk8lX0WgnFtCOwj8hm3Dvq7vzt/x9noeG2SDNGH4YNULxWDrwu # 36x0XH7zKqHOpEk8KnedIKRnRezryBAlGQ== # SIG # End signature block |