Public/AzStackHci.PageFileSettings.ps1

# ///////////////////////////////////////////////////////////////////
# Strict Mode v1 (PS 5.1 safe) - surfaces reads of uninitialised variables at runtime.
Set-StrictMode -Version 1.0

# Get-AzStackHciPageFileSettings Function
# Used to get information about the current page file settings
# on the system.
# ///////////////////////////////////////////////////////////////////
Function Get-AzStackHciPageFileSettings {
    <#
    .SYNOPSIS
 
    Gets current page file settings
 
    .DESCRIPTION
 
    Queries the system for current page file settings.
    Use -Scope Cluster to collect settings from all nodes in the failover cluster.
 
    #>


    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param (
        # Scope: 'Node' (default) for local node only, 'Cluster' to collect from all cluster nodes
        [Parameter(Mandatory=$false)]
        [ValidateSet('Node','Cluster')]
        [string]$Scope = 'Node',

        [switch]$NoOutput
    )

    begin {
        # Requires administrator permissions to get page file settings and write to log file to disk
        if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." }

        $DateFormatted = Get-Date -f "yyyyMMdd-HHmm"
        if(-not(Test-Path "C:\ProgramData\AzStackHci.DiagnosticSettings\"))
        {
            try {
                New-Item "C:\ProgramData\AzStackHci.DiagnosticSettings\" -ItemType Directory -ErrorAction Stop | Out-Null
            } catch {
                Throw "Failed to create output directory 'C:\ProgramData\AzStackHci.DiagnosticSettings\': $($_.Exception.Message)"
            }
        }
    }

    process {
        # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active
        $script:SilentMode = $false

        # Handle -NoOutput: suppress all console output
        if ($NoOutput.IsPresent) {
            $script:SilentMode = $true
            $VerbosePreference = 'SilentlyContinue'
            $DebugPreference = 'SilentlyContinue'
        }

        # ---------------------------------------------------------------
        # Cluster scope: fan out to all cluster nodes via Invoke-Command
        # ---------------------------------------------------------------
        if ($Scope -eq 'Cluster') {
            if (-not (Test-CommandExists Get-Cluster)) {
                throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node."
            }
            try {
                [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name
                [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop
            } catch {
                throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)"
            }

            Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose

            # Self-contained scriptblock — does NOT depend on the module being installed on remote nodes
            $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock {
                $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Error = $null }
                try {
                    $DateFormatted = Get-Date -f 'yyyyMMdd-HHmm'
                    $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings'
                    if (-not (Test-Path $BackupDir)) { New-Item $BackupDir -ItemType Directory -Force | Out-Null }
                    $BackupFile = "$BackupDir\PageFile_Settings_$DateFormatted.json"

                    [bool]$PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile

                    $PageFileConfig = $null
                    if ($PageFileAutoManaged -eq $false) {
                        $pfSetting = Get-CimInstance -ClassName Win32_PageFileSetting
                        if ($pfSetting) {
                            $PageFileConfig = @{
                                Name = $pfSetting.Name
                                InitialSize = $pfSetting.InitialSize
                                MaximumSize = $pfSetting.MaximumSize
                                Caption = $pfSetting.Caption
                                Description = $pfSetting.Description
                                SettingID = $pfSetting.SettingID
                            }
                        }
                    }

                    $PageFileUsage = Get-CimInstance Win32_PageFileUsage
                    $backupObject = [PSCustomObject]@{
                        BackupDate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
                        Node = $env:COMPUTERNAME
                        PageFileAutoManaged = $PageFileAutoManaged
                        PageFileConfig = $PageFileConfig
                        PageFileUsage = @{
                            AllocatedBaseSize = $PageFileUsage.AllocatedBaseSize
                            CurrentUsage = $PageFileUsage.CurrentUsage
                            PeakUsage = $PageFileUsage.PeakUsage
                            Name = $PageFileUsage.Name
                        }
                    }
                    [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false)))

                    $result.Success = $true
                    $result | Add-Member -NotePropertyName PageFileAutoManaged      -NotePropertyValue $PageFileAutoManaged
                    $result | Add-Member -NotePropertyName PageFileName             -NotePropertyValue $(if ($PageFileConfig) { $PageFileConfig.Name } else { $null })
                    $result | Add-Member -NotePropertyName PageFileInitialSize      -NotePropertyValue $(if ($PageFileConfig) { $PageFileConfig.InitialSize } else { $null })
                    $result | Add-Member -NotePropertyName PageFileMaximumSize      -NotePropertyValue $(if ($PageFileConfig) { $PageFileConfig.MaximumSize } else { $null })
                    $result | Add-Member -NotePropertyName PageFileAllocatedBaseSize -NotePropertyValue $PageFileUsage.AllocatedBaseSize
                    $result | Add-Member -NotePropertyName PageFileCurrentUsage     -NotePropertyValue $PageFileUsage.CurrentUsage
                    $result | Add-Member -NotePropertyName PageFilePeakUsage        -NotePropertyValue $PageFileUsage.PeakUsage
                } catch {
                    $result.Error = $_.Exception.Message
                }
                $result
            } -ErrorAction SilentlyContinue -ErrorVariable remoteErrors

            # Report per-node results
            if ($remoteErrors) {
                foreach ($err in $remoteErrors) {
                    Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose
                }
            }
            $successCount = 0
            $failCount = 0
            foreach ($nodeResult in $remoteResults) {
                if ($nodeResult.Success) {
                    Write-Verbose " $($nodeResult.Node): SUCCESS - backup file created" -Verbose
                    $successCount++
                } else {
                    Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)" -Verbose
                    $failCount++
                }
            }
            Write-Verbose "Cluster collection complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose
            if ($failCount -gt 0) {
                Write-Warning "$failCount node(s) failed. Review the output above for details."
            }

            # Build PSCustomObject array with settings from all nodes
            $outputObjects = foreach ($nodeResult in $remoteResults) {
                [PSCustomObject]@{
                    Node                      = $nodeResult.Node
                    Success                   = $nodeResult.Success
                    PageFileAutoManaged       = $nodeResult.PageFileAutoManaged
                    PageFileName              = $nodeResult.PageFileName
                    PageFileInitialSize       = $nodeResult.PageFileInitialSize
                    PageFileMaximumSize       = $nodeResult.PageFileMaximumSize
                    PageFileAllocatedBaseSize  = $nodeResult.PageFileAllocatedBaseSize
                    PageFileCurrentUsage      = $nodeResult.PageFileCurrentUsage
                    PageFilePeakUsage         = $nodeResult.PageFilePeakUsage
                    Error                     = $nodeResult.Error
                }
            }
            return $outputObjects
        }

        try {
            # Get current page file settings
            [bool]$script:PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile
        } catch {
            Write-Verbose "Failed to get current page file settings. Error: $($_.Exception.Message)" -Verbose
            Throw "Failed to get current page file settings. Error: $($_.Exception.Message)"
        }

        # Collect page file configuration
        $pfConfig = $null
        if (-not $script:PageFileAutoManaged) {
            Write-Verbose "Current Settings: Page File automatic management is Disabled" -Verbose
            try { $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting } catch { throw "Failed to get page file settings: $($_.Exception.Message)" }
            if ($script:PageFileConfiguration) {
                $pfConfig = @{
                    Name = $script:PageFileConfiguration.Name
                    InitialSize = $script:PageFileConfiguration.InitialSize
                    MaximumSize = $script:PageFileConfiguration.MaximumSize
                    Caption = $script:PageFileConfiguration.Caption
                    Description = $script:PageFileConfiguration.Description
                    SettingID = $script:PageFileConfiguration.SettingID
                }
                Write-Verbose " Name: $($pfConfig.Name), InitialSize: $($pfConfig.InitialSize), MaximumSize: $($pfConfig.MaximumSize)" -Verbose
            }
        } else {
            Write-Verbose "Current Settings: Page File automatic management is Enabled" -Verbose
        }

        # Collect page file usage
        try { $script:PageFileUsage = Get-CimInstance Win32_PageFileUsage } catch { throw "Failed to get page file usage: $($_.Exception.Message)" }
        $script:PageFileAllocatedBaseSize = $PageFileUsage.AllocatedBaseSize
        $script:PageFileCurrentUsage = $PageFileUsage.CurrentUsage
        $script:PageFilePeakUsage = $PageFileUsage.PeakUsage
        Write-Verbose "Page File usage: AllocatedBaseSize=$($script:PageFileAllocatedBaseSize), CurrentUsage=$($script:PageFileCurrentUsage), PeakUsage=$($script:PageFilePeakUsage)" -Verbose

        # Save backup as JSON
        $backupObject = [PSCustomObject]@{
            BackupDate = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
            Node = $env:COMPUTERNAME
            PageFileAutoManaged = $script:PageFileAutoManaged
            PageFileConfig = $pfConfig
            PageFileUsage = @{
                AllocatedBaseSize = $PageFileUsage.AllocatedBaseSize
                CurrentUsage = $PageFileUsage.CurrentUsage
                PeakUsage = $PageFileUsage.PeakUsage
                Name = $PageFileUsage.Name
            }
        }
        $BackupFile = "C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_$DateFormatted.json"
        [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false)))
        Write-Verbose "Current Page File settings exported to '$BackupFile'" -Verbose

        Write-HostAzS `n

        # Return a PSCustomObject for easy reporting
        [PSCustomObject]@{
            Node                     = $env:COMPUTERNAME
            Success                  = $true
            PageFileAutoManaged      = $script:PageFileAutoManaged
            PageFileName             = if ($script:PageFileConfiguration) { $script:PageFileConfiguration.Name } else { $null }
            PageFileInitialSize      = if ($script:PageFileConfiguration) { $script:PageFileConfiguration.InitialSize } else { $null }
            PageFileMaximumSize      = if ($script:PageFileConfiguration) { $script:PageFileConfiguration.MaximumSize } else { $null }
            PageFileAllocatedBaseSize = $script:PageFileAllocatedBaseSize
            PageFileCurrentUsage     = $script:PageFileCurrentUsage
            PageFilePeakUsage        = $script:PageFilePeakUsage
            Error                    = $null
        }

    } # End of process block

    end {
        if ($NoOutput.IsPresent) { $script:SilentMode = $false }
        Write-Debug "Completed Get-AzStackHciPageFileSettings function"
    }
} # End of Get-AzStackHciPageFileSettings


# ///////////////////////////////////////////////////////////////////
# Set-AzStackHciPageFileSettings Function
# Used to set a manual page file on the system with a specified size,
# or auto-sized for kernel dump support.
# ///////////////////////////////////////////////////////////////////
Function Set-AzStackHciPageFileSettings {
    <#
    .SYNOPSIS
 
    Sets page file settings to a specified size or auto-sizes for kernel dump support
 
    .DESCRIPTION
 
    Configures a fixed-size manual page file, disabling automatic page file management.
 
    Use -KernelDumpRecommended to auto-size the page file based on system memory, using the same
    calculation as Set-AzStackHciMemoryDumpSettings (64 GiB for <768 GB RAM, 128 GiB for >=768 GB RAM).
    This ensures the page file is large enough to stage a kernel memory dump.
 
    Or specify -InitialSizeMB and -MaximumSizeMB for explicit sizing.
 
    Use -Scope Cluster to apply settings to all nodes in the failover cluster via Invoke-Command.
    The scriptblock is self-contained and does not require the module on remote nodes.
 
    Reference: https://learn.microsoft.com/en-us/windows-server/administration/server-core/server-core-memory-dump
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([PSCustomObject[]])]
    param (
        # Path to drive letter for PageFile
        [Parameter(Mandatory=$true, Position=1)]
        [ValidateScript({Test-Path $_})]
        [String]
        [ValidatePattern('^[C-Zc-z]:\\?$')]$PageFileFileDriveLetter,

        # Auto-size for kernel dump support (uses same calculation as Set-AzStackHciMemoryDumpSettings)
        [Parameter(Mandatory=$true, ParameterSetName='KernelDumpRecommended')]
        [Switch]
        $KernelDumpRecommended,

        # Initial page file size in MB (manual sizing)
        [Parameter(Mandatory=$true, ParameterSetName='ManualSize')]
        [ValidateRange(1024, 262144)]
        [uint32]$InitialSizeMB,

        # Maximum page file size in MB (manual sizing)
        [Parameter(Mandatory=$true, ParameterSetName='ManualSize')]
        [ValidateRange(1024, 262144)]
        [uint32]$MaximumSizeMB,

        # Scope: 'Node' (default) for local node only, 'Cluster' to apply to all cluster nodes
        [Parameter(Mandatory=$false)]
        [ValidateSet('Node','Cluster')]
        [string]$Scope = 'Node',

        [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")]
        [switch]$NoOutput
    )

    begin {
        if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." }

        # Backup current page file settings (skip in Cluster mode — each node backs up inline)
        if ($Scope -ne 'Cluster') {
            $null = Get-AzStackHciPageFileSettings
        }
    }

    process {
        # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active
        $script:SilentMode = $false

        if ($NoOutput.IsPresent) {
            $script:SilentMode = $true
            $VerbosePreference = 'SilentlyContinue'
            $DebugPreference = 'SilentlyContinue'
        }

        # Determine target page file size
        if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') {
            # Use same calculation as Set-AzStackHciMemoryDumpSettings / SetDedicatedDumpFileSize
            [uint32]$TargetSizeMB = SetDedicatedDumpFileSize
            if (-not $TargetSizeMB) { throw "Failed to calculate recommended page file size based on system memory." }
            $InitialSizeMB = $TargetSizeMB
            $MaximumSizeMB = $TargetSizeMB
        }

        if ($MaximumSizeMB -lt $InitialSizeMB) {
            throw "-MaximumSizeMB ($MaximumSizeMB) must be greater than or equal to -InitialSizeMB ($InitialSizeMB)."
        }

        $targetSizeGiB = [math]::Round($MaximumSizeMB / 1024, 1)

        # ---------------------------------------------------------------
        # Cluster scope: fan out to all cluster nodes via Invoke-Command
        # ---------------------------------------------------------------
        if ($Scope -eq 'Cluster') {
            if (-not (Test-CommandExists Get-Cluster)) {
                throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node."
            }
            try {
                [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name
                [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop
            } catch {
                throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)"
            }

            Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose

            $sizeDescription = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') {
                "Set kernel-dump-recommended page file (${targetSizeGiB} GiB) on $($Nodes.Count) cluster nodes"
            } else {
                "Set manual page file (Initial: ${InitialSizeMB} MB, Max: ${MaximumSizeMB} MB) on $($Nodes.Count) cluster nodes"
            }
            if (-not $PSCmdlet.ShouldProcess("Cluster '$ClusterName' ($($Nodes -join ', '))", $sizeDescription)) {
                return
            }

            $remoteArgs = @{
                DriveLetter    = $PageFileFileDriveLetter
                InitialSizeMB  = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { 0 } else { $InitialSizeMB }
                MaximumSizeMB  = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { 0 } else { $MaximumSizeMB }
                AutoSize       = ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended')
            }

            $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock {
                param($Params)
                $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Action = $null; PageFileSizeMB = 0; Error = $null }
                try {
                    $DriveLetter = $Params.DriveLetter
                    if ($DriveLetter.Length -eq 2) { $DriveLetter = "$DriveLetter\" }

                    # Calculate size if auto-sizing
                    # Tiers: <=128 GiB → 32 GiB, 129-767 GiB → 64 GiB, >=768 GiB → 128 GiB
                    if ($Params.AutoSize) {
                        $totalPhysicalMemory = (Get-CimInstance -ClassName Win32_PhysicalMemory -ErrorAction Stop | Measure-Object -Property Capacity -Sum).Sum
                        [uint32]$InitSize = if ($totalPhysicalMemory -ge 768GB) { 131072 } elseif ($totalPhysicalMemory -le 128GB) { 32768 } else { 65536 }
                        [uint32]$MaxSize = $InitSize
                    } else {
                        [uint32]$InitSize = $Params.InitialSizeMB
                        [uint32]$MaxSize = $Params.MaximumSizeMB
                    }

                    # Get auto-managed state BEFORE the "already configured" check
                    $PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).AutomaticManagedPagefile

                    # Check if current settings already match the target
                    $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
                    if (-not $PageFileAutoManaged -and
                        $pfConfig -and
                        ($pfConfig | Measure-Object).Count -eq 1 -and
                        $pfConfig.Name -eq "${DriveLetter}pagefile.sys" -and
                        $pfConfig.InitialSize -eq $InitSize -and
                        $pfConfig.MaximumSize -eq $MaxSize) {
                        $result.Success = $true
                        $result.Action = 'NoChangeRequired'
                        $result.PageFileSizeMB = $MaxSize
                        return $result
                    }

                    # Backup current settings (PageFileAutoManaged already queried above)
                    $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings'
                    if (-not (Test-Path $BackupDir)) { New-Item $BackupDir -ItemType Directory -Force | Out-Null }
                    $DateFormatted = Get-Date -f 'yyyyMMdd-HHmm'
                    $BackupFile = "$BackupDir\PageFile_Settings_$DateFormatted.json"
                    $pfConfig = $null
                    if (-not $PageFileAutoManaged) {
                        $pfSetting = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
                        if ($pfSetting) {
                            $pfConfig = @{
                                Name = $pfSetting.Name
                                InitialSize = $pfSetting.InitialSize
                                MaximumSize = $pfSetting.MaximumSize
                                Caption = $pfSetting.Caption
                                Description = $pfSetting.Description
                                SettingID = $pfSetting.SettingID
                            }
                        }
                    }
                    $pfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue
                    $backupObject = [PSCustomObject]@{
                        BackupDate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
                        Node = $env:COMPUTERNAME
                        PageFileAutoManaged = $PageFileAutoManaged
                        PageFileConfig = $pfConfig
                        PageFileUsage = @{
                            AllocatedBaseSize = if ($pfUsage) { $pfUsage.AllocatedBaseSize } else { $null }
                            CurrentUsage = if ($pfUsage) { $pfUsage.CurrentUsage } else { $null }
                            PeakUsage = if ($pfUsage) { $pfUsage.PeakUsage } else { $null }
                            Name = if ($pfUsage) { $pfUsage.Name } else { $null }
                        }
                    }
                    [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false)))

                    # Check disk space: page file needs InitSize MB on the drive
                    $Disk = Get-PSDrive -ErrorAction Stop | Where-Object { $_.Root -eq $DriveLetter }
                    if (-not $Disk) { throw "Drive '$DriveLetter' not found on $env:COMPUTERNAME." }
                    $currentPfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "$($DriveLetter.TrimEnd('\'))\*" }
                    $currentPfSizeMB = if ($currentPfUsage) { $currentPfUsage.AllocatedBaseSize } else { 0 }
                    # Net new space = target size - current page file size (if on same drive)
                    $netNewMB = [math]::Max(0, $MaxSize - $currentPfSizeMB)
                    $freeGiB = [math]::Round($Disk.Free / 1GB, 2)
                    $neededGiB = [math]::Round($netNewMB / 1024 + 10, 1)  # +10 GiB OS margin
                    if ($freeGiB -lt $neededGiB) {
                        throw "Insufficient disk space on $DriveLetter for ${MaxSize} MB page file. Free: ${freeGiB} GiB, Needed: ${neededGiB} GiB."
                    }

                    # Disable auto-management if enabled
                    if ($PageFileAutoManaged) {
                        $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
                        $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } -ErrorAction Stop
                    }

                    # Remove existing page file config
                    $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
                    if ($pfConfig) { $pfConfig | Remove-CimInstance -ErrorAction Stop }

                    # Create new page file
                    $newPf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${DriveLetter}pagefile.sys" } -ErrorAction Stop
                    $newPf | Set-CimInstance -Property @{ InitialSize = $InitSize; MaximumSize = $MaxSize } -ErrorAction Stop

                    $result.PageFileSizeMB = $MaxSize
                    $result.Success = $true
                } catch {
                    $result.Error = $_.Exception.Message
                }
                $result
            } -ArgumentList (,$remoteArgs) -ErrorAction SilentlyContinue -ErrorVariable remoteErrors

            if ($remoteErrors) { foreach ($err in $remoteErrors) { Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose } }
            $successCount = 0; $failCount = 0; $noChangeCount = 0
            foreach ($nodeResult in $remoteResults) {
                if ($nodeResult.Success -and $nodeResult.Action -eq 'NoChangeRequired') {
                    Write-Verbose " $($nodeResult.Node): Already configured ($($nodeResult.PageFileSizeMB) MB) — no changes needed" -Verbose
                    $noChangeCount++; $successCount++
                } elseif ($nodeResult.Success) { Write-Verbose " $($nodeResult.Node): SUCCESS ($($nodeResult.PageFileSizeMB) MB)" -Verbose; $successCount++ }
                else { Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)" -Verbose; $failCount++ }
            }
            Write-Verbose "Cluster operation complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose
            if ($failCount -gt 0) { Write-Warning "$failCount node(s) failed. Review the output above for details." }
            return $remoteResults
        }

        # ---------------------------------------------------------------
        # Node scope: set manual page file on local node
        # ---------------------------------------------------------------

        # Normalize drive letter
        if ($PageFileFileDriveLetter.Length -eq 2) { $PageFileFileDriveLetter = "$PageFileFileDriveLetter\" }

        # Check if current settings already match the target
        $dumpDrive = $PageFileFileDriveLetter.TrimEnd('\')
        [bool]$PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).AutomaticManagedPagefile
        $currentPfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
        $currentPfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "${dumpDrive}\*" }
        $currentPfSizeMB = if ($currentPfUsage) { $currentPfUsage.AllocatedBaseSize } else { 0 }

        if (-not $PageFileAutoManaged -and
            $currentPfConfig -and
            ($currentPfConfig | Measure-Object).Count -eq 1 -and
            $currentPfConfig.Name -eq "${PageFileFileDriveLetter}pagefile.sys" -and
            $currentPfConfig.InitialSize -eq $InitialSizeMB -and
            $currentPfConfig.MaximumSize -eq $MaximumSizeMB) {
            Write-Verbose "Page file is already configured as requested: ${PageFileFileDriveLetter}pagefile.sys (Initial: ${InitialSizeMB} MB, Max: ${MaximumSizeMB} MB). No changes needed." -Verbose
            return [PSCustomObject]@{
                Node = $env:COMPUTERNAME
                Success = $true
                Action = 'NoChangeRequired'
                InitialSizeMB = $InitialSizeMB
                MaximumSizeMB = $MaximumSizeMB
                PreviousAutoManaged = $false
                Error = $null
            }
        }

        # Check disk space
        $Disk = Get-PSDrive -ErrorAction Stop | Where-Object { $_.Root -eq $PageFileFileDriveLetter }
        if (-not $Disk) { throw "Drive '$PageFileFileDriveLetter' not found." }
        $netNewMB = [math]::Max(0, $MaximumSizeMB - $currentPfSizeMB)
        $freeGiB = [math]::Round($Disk.Free / 1GB, 2)
        $neededGiB = [math]::Round($netNewMB / 1024 + 10, 1)
        if ($freeGiB -lt $neededGiB) {
            throw "Insufficient disk space on '$PageFileFileDriveLetter' for ${MaximumSizeMB} MB page file. Free: ${freeGiB} GiB, Needed: ~${neededGiB} GiB (${netNewMB} MB net new + 10 GiB OS margin). Current page file on this drive: ${currentPfSizeMB} MB."
        }

        # ShouldProcess gate
        $actionDescription = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') {
            "Set kernel-dump-recommended manual page file (${InitialSizeMB} MB / ${targetSizeGiB} GiB) on ${PageFileFileDriveLetter}"
        } else {
            "Set manual page file (Initial: ${InitialSizeMB} MB, Max: ${MaximumSizeMB} MB) on ${PageFileFileDriveLetter}"
        }
        if (-not $PSCmdlet.ShouldProcess("Page file on $PageFileFileDriveLetter", $actionDescription)) {
            return
        }

        # Disable auto-management if enabled (already queried above)
        if ($PageFileAutoManaged) {
            Write-Verbose "Disabling automatic page file management..." -Verbose
            $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
            $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } -ErrorAction Stop
            Write-Verbose "Automatic page file management disabled." -Verbose
        }

        # Remove existing page file config
        $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
        if ($pfConfig) {
            if (($pfConfig | Measure-Object).Count -eq 1) {
                $pfConfig | Remove-CimInstance -ErrorAction Stop
                Write-Verbose "Existing page file settings removed." -Verbose
            } else {
                throw "Unexpected: more than one page file configured (found $(($pfConfig | Measure-Object).Count)). Remove extra page files manually first."
            }
        }

        # Create new page file with specified size
        try {
            $PageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${PageFileFileDriveLetter}pagefile.sys" } -ErrorAction Stop
            $PageFile | Set-CimInstance -Property @{ InitialSize = $InitialSizeMB; MaximumSize = $MaximumSizeMB } -ErrorAction Stop
        } catch {
            throw "Failed to create page file with InitialSize=${InitialSizeMB} MB, MaximumSize=${MaximumSizeMB} MB. Error: $($_.Exception.Message)"
        }

        if (-not $PageFile) { throw "Failed to configure page file." }

        Write-Verbose "Page file configured: ${PageFileFileDriveLetter}pagefile.sys (InitialSize: ${InitialSizeMB} MB, MaximumSize: ${MaximumSizeMB} MB / ${targetSizeGiB} GiB)" -Verbose
        Write-Verbose "Automatic page file management has been disabled." -Verbose
        Write-Verbose "A system restart is required for page file changes to take effect." -Verbose

        [PSCustomObject]@{
            Node = $env:COMPUTERNAME
            Success = $true
            Action = if ($PSCmdlet.ParameterSetName -eq 'KernelDumpRecommended') { 'SetPageFileKernelDumpRecommended' } else { 'SetPageFileManual' }
            InitialSizeMB = $InitialSizeMB
            MaximumSizeMB = $MaximumSizeMB
            PreviousAutoManaged = $PageFileAutoManaged
            Error = $null
        }

    } # End of process block

    end {
        if ($NoOutput.IsPresent) { $script:SilentMode = $false }
        Write-Debug "Completed Set-AzStackHciPageFileSettings function"
    }

} # End of Set-AzStackHciPageFileSettings


# ///////////////////////////////////////////////////////////////////
# Set-AzStackHciPageFileSettingsMinimal Function
# Used to set a static 4GB page file on the system, target drive is
# the only parameter.
# ///////////////////////////////////////////////////////////////////
Function Set-AzStackHciPageFileSettingsMinimal {
    <#
    .SYNOPSIS
 
    Sets page file settings to minimum 4GB fixed size
 
    .DESCRIPTION
 
    Queries the system for current page file settings and sets a fixed 4GB page file.
 
    Use -Scope Cluster to apply settings to all nodes in the failover cluster via Invoke-Command.
    The scriptblock is self-contained and does not require the module on remote nodes.
 
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([PSCustomObject[]])]
    param (
        # Path to drive letter for PageFile
        [Parameter(Mandatory=$true,Position=1)]
        [ValidateScript({Test-Path $_})]
        [String]
        [ValidatePattern('^[C-Zc-z]:\\?$')]$PageFileFileDriveLetter, # Drive letter for the page file, must be a valid drive letter (C: through Z:)

        # Scope: 'Node' (default) for local node only, 'Cluster' to apply to all cluster nodes
        [Parameter(Mandatory=$false)]
        [ValidateSet('Node','Cluster')]
        [string]$Scope = 'Node',

        [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")]
        [switch]$NoOutput
    )

    begin {
        # Requires administrator permissions to set page file settings
        if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." }

        # Call function to get current Page File settings and save to backup log file (skip in Cluster mode — each node backs up inline)
        if ($Scope -ne 'Cluster') {
            $null = Get-AzStackHciPageFileSettings
        }
    }

    process {
        # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active
        $script:SilentMode = $false

        # Handle -NoOutput: suppress all console output
        if ($NoOutput.IsPresent) {
            $script:SilentMode = $true
            $VerbosePreference = 'SilentlyContinue'
            $DebugPreference = 'SilentlyContinue'
        }

        # ---------------------------------------------------------------
        # Cluster scope: fan out to all cluster nodes via Invoke-Command
        # ---------------------------------------------------------------
        if ($Scope -eq 'Cluster') {
            if (-not (Test-CommandExists Get-Cluster)) {
                throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node."
            }
            try {
                [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name
                [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop
            } catch {
                throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)"
            }

            Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose

            if (-not $PSCmdlet.ShouldProcess("Cluster '$ClusterName' ($($Nodes -join ', '))", "Set fixed 4GB page file on $($Nodes.Count) cluster nodes")) {
                return
            }

            $remoteDriveLetter = $PageFileFileDriveLetter

            # Self-contained scriptblock — does NOT depend on the module being installed on remote nodes
            $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock {
                param($DriveLetter)
                $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Error = $null }
                try {
                    # Normalize drive letter
                    if ($DriveLetter.Length -eq 2) { $DriveLetter = "$DriveLetter\" }

                    # ── Inline backup of current page file settings ──
                    $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings'
                    if (-not (Test-Path $BackupDir)) { New-Item $BackupDir -ItemType Directory -Force | Out-Null }
                    $DateFormatted = Get-Date -f 'yyyyMMdd-HHmm'
                    $BackupFile = "$BackupDir\PageFile_Settings_$DateFormatted.json"
                    $PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).AutomaticManagedPagefile

                    $pfConfigBackup = $null
                    if (-not $PageFileAutoManaged) {
                        $pfSetting = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
                        if ($pfSetting) {
                            $pfConfigBackup = @{
                                Name = $pfSetting.Name
                                InitialSize = $pfSetting.InitialSize
                                MaximumSize = $pfSetting.MaximumSize
                                Caption = $pfSetting.Caption
                                Description = $pfSetting.Description
                                SettingID = $pfSetting.SettingID
                            }
                        }
                    }
                    $pfUsage = Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue
                    $backupObject = [PSCustomObject]@{
                        BackupDate = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
                        Node = $env:COMPUTERNAME
                        PageFileAutoManaged = $PageFileAutoManaged
                        PageFileConfig = $pfConfigBackup
                        PageFileUsage = @{
                            AllocatedBaseSize = if ($pfUsage) { $pfUsage.AllocatedBaseSize } else { $null }
                            CurrentUsage = if ($pfUsage) { $pfUsage.CurrentUsage } else { $null }
                            PeakUsage = if ($pfUsage) { $pfUsage.PeakUsage } else { $null }
                            Name = if ($pfUsage) { $pfUsage.Name } else { $null }
                        }
                    }
                    [System.IO.File]::WriteAllText($BackupFile, ($backupObject | ConvertTo-Json -Depth 3), (New-Object System.Text.UTF8Encoding($false)))

                    # ── Set 4GB fixed page file ──
                    if (-not $PageFileAutoManaged) {
                        # Manual: remove existing page file config, create 4GB fixed
                        $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
                        if ($pfConfig) {
                            if (($pfConfig | Measure-Object).Count -eq 1) {
                                $pfConfig | Remove-CimInstance -ErrorAction Stop
                            } else {
                                throw "Unexpected: more than one page file configured (found $(($pfConfig | Measure-Object).Count))"
                            }
                        }
                        $newPf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${DriveLetter}pagefile.sys" } -ErrorAction Stop
                        $newPf | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } -ErrorAction Stop
                    } else {
                        # Auto-managed: disable auto management, then create 4GB fixed
                        $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
                        $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false } -ErrorAction Stop

                        $pfConfig = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
                        if ($pfConfig) {
                            if (($pfConfig | Measure-Object).Count -eq 1) {
                                $pfConfig | Remove-CimInstance -ErrorAction Stop
                            } else {
                                throw "Unexpected: more than one page file configured (found $(($pfConfig | Measure-Object).Count))"
                            }
                        }
                        $newPf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = "${DriveLetter}pagefile.sys" } -ErrorAction Stop
                        $newPf | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 } -ErrorAction Stop
                    }

                    if (-not $newPf) { throw 'Failed to configure page file' }
                    $result.Success = $true
                } catch {
                    $result.Error = $_.Exception.Message
                }
                $result
            } -ArgumentList $remoteDriveLetter -ErrorAction SilentlyContinue -ErrorVariable remoteErrors

            # Report per-node results
            if ($remoteErrors) {
                foreach ($err in $remoteErrors) {
                    Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose
                }
            }
            $successCount = 0
            $failCount = 0
            foreach ($nodeResult in $remoteResults) {
                if ($nodeResult.Success) {
                    Write-Verbose " $($nodeResult.Node): SUCCESS" -Verbose
                    $successCount++
                } else {
                    Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)" -Verbose
                    $failCount++
                }
            }
            Write-Verbose "Cluster operation complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose
            if ($failCount -gt 0) {
                Write-Warning "$failCount node(s) failed. Review the output above for details."
            }
            return $remoteResults
        }

        # if the drive letter is only 2 characters (C:), add a backslash to the end (C:\)
        if($PageFileFileDriveLetter.Length -eq 2){
            $PageFileFileDriveLetter = $($PageFileFileDriveLetter + "\")
        }

        # ShouldProcess gate: prompt user before modifying page file settings
        if (-not $PSCmdlet.ShouldProcess("Page file on $PageFileFileDriveLetter", "Set fixed 4GB page file")) {
            return
        }

        try {
            # Get current page file settings
            [bool]$script:PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile
        } catch {
            Write-Verbose "Failed to get current page file settings. Error: $($_.Exception.Message)" -Verbose
            Throw "Failed to get current page file settings. Error: $($_.Exception.Message)"
        }
        
        # If page file is NOT automatically managed
        if($script:PageFileAutoManaged -eq $False){
            # Page File manual settings
            Write-HostAzS "Automatic management of page file already disabled"
            Write-HostAzS "Configuring a fixed page file of 4GB size using file: $($PageFileFileDriveLetter)pagefile.sys`n"
            try {
                $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting
            } catch {
                Write-Verbose "Failed to get existing page file settings. Error: $($_.Exception.Message)" -Verbose
                Throw "Error: Failed to get existing page file settings. Error: $($_.Exception.Message)"
            }
            # Remove existing manual page file settings
            if($PageFileConfiguration){
                if(($PageFileConfiguration | Measure-Object).Count -eq 1){
                    try {
                        # Delete existing page file settings
                        $PageFileConfiguration | Remove-CimInstance
                    } catch {
                        Write-Verbose "Failed to delete existing page file settings. Error: $($_.Exception.Message)" -Verbose
                        Throw "Error: Failed to delete existing page file settings. Error: $($_.Exception.Message)"
                    }
                    Write-Verbose "Existing page file settings removed." -Verbose
                } else {
                    Write-Verbose "Unexpected configuration, more than one page file is currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." -Verbose
                    Throw "Error: Unexpected configuration, more than one page file currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files."
                }
            } else {
                Write-Verbose "Existing page file settings not found." -Verbose
            }
            # Set a New page file, and set the size to 4GB fixed
            try 
            {
                # Use New-CimInstance to create a new page file setting, due to "Set-WmiInstance : Generic failure" error, if no existing page file settings are configured (as deleted).
                $PageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name= "$($PageFileFileDriveLetter)pagefile.sys" }
                $PageFile | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 }
            } catch {
                Write-Verbose "Failed to set minimum page file settings. Error: $($_.Exception.Message)" -Verbose
                Throw "Failed to set minimum page file settings. Error: $($_.Exception.Message)"
            }
            if($PageFile){
                Write-Verbose "Page File settings updated successfully to a fixed size of 4GB" -Verbose
                Write-Verbose "Page File: = '$($pagefile.Description)'" -Verbose
            } else {
                Write-Verbose "Failed to configure minimum page file settings." -Verbose
                Throw "Error: Failed to configure minimum page file settings."
            }

            [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'SetMinimumPageFile'; PreviousAutoManaged = $false; Error = $null }

        } elseif($script:PageFileAutoManaged -eq $True){
            # If the page file is currently automatically managed
            Write-HostAzS "Disabling automatic management of page file"
            # Remove System Managed:
            try {
                $ComputerSystem = Get-CimInstance -ClassName Win32_ComputerSystem
            }
            catch {
                Write-Verbose "Failed to get existing automatic management settings. Error: $($_.Exception.Message)" -Verbose
                Throw "Error: Failed to get existing automatic management settings. Error: $($_.Exception.Message)"
            }
            try {
                $ComputerSystem | Set-CimInstance -Property @{ AutomaticManagedPagefile = $false }
            }
            catch {
                Write-Verbose "Failed to disable automatic management of page file. Error: $($_.Exception.Message)" -Verbose
                Throw "Error: Failed to disable automatic management of page file. Error: $($_.Exception.Message)"
            }
            Write-Verbose "Automatic management of page file disabled" -Verbose

            try {
                $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting
            } catch {
                Write-Verbose "Failed to get existing page file settings. Error: $($_.Exception.Message)" -Verbose
                Throw "Error: Failed to get existing page file settings. Error: $($_.Exception.Message)"
            }

            Write-HostAzS "Configuring a fixed page file of 4GB size using file: $($PageFileFileDriveLetter)pagefile.sys`n"
            # Remove existing page file setting and set a new page file, and set the size to 4GB fixed
            if($PageFileConfiguration){
                if(($PageFileConfiguration | Measure-Object).Count -eq 1){
                    try {
                        # Delete existing page file settings
                        $PageFileConfiguration | Remove-CimInstance
                    } catch {
                        Write-Verbose "Failed to delete existing page file settings. Error: $($_.Exception.Message)" -Verbose
                        Throw "Error: Failed to delete existing page file settings. Error: $($_.Exception.Message)"
                    }
                    Write-Verbose "Existing page file settings removed." -Verbose
                } else {
                    Write-Verbose "Unexpected configuration, more than one page file is currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files." -Verbose
                    Throw "Error: Unexpected configuration, more than one page file currently configured! Expected 1, but found $(($PageFileConfiguration | Measure-Object).Count) x page files."
                }

                # Set a New page file, and set the size to 4GB fixed
                try 
                {
                    # Use New-CimInstance to create a new page file setting, due to "Set-WmiInstance : Generic failure" error, if no existing page file settings are configured (as deleted).
                    $PageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name= "$($PageFileFileDriveLetter)pagefile.sys" }
                    $PageFile | Set-CimInstance -Property @{ InitialSize = 4096; MaximumSize = 4096 }
                } catch {
                    Write-Verbose "Failed to set new page file settings. Error: $($_.Exception.Message)" -Verbose
                    Throw "Failed to set new page file settings. Error: $($_.Exception.Message)"
                }
                if($PageFile){
                    Write-Verbose "Page File settings updated successfully to a fixed size of 4GB" -Verbose
                    Write-Verbose "Page File: = '$($pagefile.Description)'" -Verbose
                } else {
                    Write-Verbose "Failed to configure minimum page file settings." -Verbose
                    Throw "Error: Failed to configure minimum page file settings."
                }
                Write-Verbose "Page File settings updated to minimum recommended configuration, (static size of 4GB)." -Verbose

            } # End of If PageFileConfiguration
            
            Write-Verbose "A system restart is required for Page File changes to take effect.`n`n" -Verbose

            [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'SetMinimumPageFile'; PreviousAutoManaged = $true; Error = $null }

        } else {
            Write-Error "Current Settings: Page File automatic management is Unknown (not enabled or disabled)." -Verbose
            Throw "Error: Current Settings: Page File automatic management is Unknown (not enabled or disabled)."
        } # End of If PageFileAutoManaged

    } # End of process block

    end {
        if ($NoOutput.IsPresent) { $script:SilentMode = $false }
        Write-Debug "Completed Set-AzStackHciPageFileSettingsMinimal function"
    }

} # End of Set-AzStackHciPageFileSettingsMinimal


# ///////////////////////////////////////////////////////////////////
# Restore-AzStackHciPageFileSettings function
# Used to restore or roll back page file settings
# ///////////////////////////////////////////////////////////////////
Function Restore-AzStackHciPageFileSettings
{
    <#
    .SYNOPSIS
 
    Restores page file settings from a backup file
 
    .DESCRIPTION
 
    Restores the page file settings from a backup file, using the 'PageFile_Settings_YYYYMMDD.json' file.
    Requires administrator permissions to restore page file settings.
 
    Use -Scope Cluster to restore settings on all nodes in the failover cluster via Invoke-Command.
    In Cluster mode, each node auto-discovers its most recent backup file if no path is specified.
    The scriptblock is self-contained and does not require the module on remote nodes.
     
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([PSCustomObject[]])]
    param (
        # File path to backup file (required for Node scope; optional for Cluster scope where auto-discovery is used)
        [Parameter(Mandatory=$false,Position=0)]
        [ValidateScript({Test-Path $_})]
        [String]$PageFileSettingsFilePath, # File used to restore settings from, must be a valid path

        # Scope: 'Node' (default) for local node only, 'Cluster' to restore on all cluster nodes
        [Parameter(Mandatory=$false)]
        [ValidateSet('Node','Cluster')]
        [string]$Scope = 'Node',

        [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")]
        [switch]$NoOutput
    )

    begin {
        # Requires administrator permissions to set page file settings
        if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." }
    }

    process {
        # Reset SilentMode at entry — ensures clean state even if a prior call threw while -NoOutput was active
        $script:SilentMode = $false

        # Handle -NoOutput: suppress all console output
        if ($NoOutput.IsPresent) {
            $script:SilentMode = $true
            $VerbosePreference = 'SilentlyContinue'
            $DebugPreference = 'SilentlyContinue'
        }

        # ---------------------------------------------------------------
        # Cluster scope: fan out to all cluster nodes via Invoke-Command
        # ---------------------------------------------------------------
        if ($Scope -eq 'Cluster') {
            if (-not (Test-CommandExists Get-Cluster)) {
                throw "The Get-Cluster cmdlet is not available. Please run this function on a clustered node."
            }
            try {
                [string]$ClusterName = (Get-Cluster -ErrorAction Stop).Name
                [array]$Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name -ErrorAction Stop
            } catch {
                throw "Failed to enumerate cluster nodes. Error: $($_.Exception.Message)"
            }

            Write-Verbose "Cluster '$ClusterName' detected with $($Nodes.Count) node(s): $($Nodes -join ', ')" -Verbose

            if (-not $PSCmdlet.ShouldProcess("Cluster '$ClusterName' ($($Nodes -join ', '))", "Restore page file settings from backup on $($Nodes.Count) cluster nodes")) {
                return
            }

            # Pass the file path if provided; otherwise each node auto-discovers its latest backup
            $remoteFilePath = if ($PSBoundParameters.ContainsKey('PageFileSettingsFilePath')) { $PageFileSettingsFilePath } else { $null }

            # Self-contained scriptblock — does NOT depend on the module being installed on remote nodes
            $remoteResults = Invoke-Command -ComputerName $Nodes -ScriptBlock {
                param($FilePath)
                $result = [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $false; Action = $null; BackupFile = $null; Error = $null }
                try {
                    $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings'

                    # Auto-discover the most recent backup file if no path was provided
                    if ([string]::IsNullOrEmpty($FilePath)) {
                        $LatestFile = Get-ChildItem "$BackupDir\PageFile_Settings_*.json" -ErrorAction SilentlyContinue |
                            Sort-Object LastWriteTime -Descending | Select-Object -First 1
                        if (-not $LatestFile) { throw "WARNING: No PageFile backup files found in '$BackupDir' on $env:COMPUTERNAME. Run Get-AzStackHciPageFileSettings first." }
                        $FilePath = $LatestFile.FullName
                    }
                    $result.BackupFile = $FilePath

                    # Validate file path
                    if ($FilePath -notlike 'C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_*') {
                        throw "Backup file path must match 'C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_*'. Got: '$FilePath'"
                    }
                    if (-not (Test-Path $FilePath)) { throw "Backup file not found: '$FilePath'" }

                    $backup = Get-Content $FilePath -Raw -ErrorAction Stop | ConvertFrom-Json
                    $wasAutoManaged = $backup.PageFileAutoManaged
                    $currentPageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile

                    if ($wasAutoManaged) {
                        # Backup had auto-managed: restore to auto-managed
                        $nameValue = $backup.PageFileUsage.Name
                        if (-not $nameValue) { throw 'Could not find page file Name in backup file' }

                        if ($currentPageFileAutoManaged) {
                            throw 'Page File is already automatically managed, no action required.'
                        }

                        # Remove current manual page file
                        $currentPf = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
                        if ($currentPf -and ($currentPf | Measure-Object).Count -eq 1) {
                            $currentPf | Remove-CimInstance -ErrorAction Stop
                        } elseif ($currentPf -and ($currentPf | Measure-Object).Count -gt 1) {
                            throw "Unexpected: $(($currentPf | Measure-Object).Count) page files configured"
                        }

                        # Create with size 0 (system-managed)
                        $pf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $nameValue }
                        $pf | Set-CimInstance -Property @{ InitialSize = 0; MaximumSize = 0 }

                        # Enable automatic management
                        $cs = Get-CimInstance -ClassName Win32_ComputerSystem
                        $cs | Set-CimInstance -Property @{ AutomaticManagedPagefile = $true }
                    } else {
                        # Backup had manual config: restore with backup's settings
                        $restoreName = $backup.PageFileConfig.Name
                        $restoreInitial = [int]$backup.PageFileConfig.InitialSize
                        $restoreMax = [int]$backup.PageFileConfig.MaximumSize
                        if (-not $restoreName -or $null -eq $restoreInitial -or $null -eq $restoreMax) {
                            throw 'Could not parse page file Name, InitialSize, or MaximumSize from backup file'
                        }

                        # Remove current config
                        $currentPf = Get-CimInstance -ClassName Win32_PageFileSetting -ErrorAction SilentlyContinue
                        if ($currentPf -and ($currentPf | Measure-Object).Count -eq 1) {
                            $currentPf | Remove-CimInstance -ErrorAction Stop
                        } elseif ($currentPf -and ($currentPf | Measure-Object).Count -gt 1) {
                            throw "Unexpected: $(($currentPf | Measure-Object).Count) page files configured"
                        }

                        # Restore with backup settings
                        $pf = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $restoreName }
                        $pf | Set-CimInstance -Property @{ InitialSize = $restoreInitial; MaximumSize = $restoreMax }
                    }

                    $result.Action = if ($wasAutoManaged) { 'RestoreAutoManaged' } else { 'RestoreManual' }
                    $result.Success = $true
                } catch {
                    $result.Error = $_.Exception.Message
                }
                $result
            } -ArgumentList $remoteFilePath -ErrorAction SilentlyContinue -ErrorVariable remoteErrors

            # Report per-node results
            if ($remoteErrors) {
                foreach ($err in $remoteErrors) {
                    Write-Verbose "REMOTE ERROR: $($err.Exception.Message)" -Verbose
                }
            }
            $successCount = 0
            $failCount = 0
            foreach ($nodeResult in $remoteResults) {
                $backupInfo = if ($nodeResult.BackupFile) { " (from $($nodeResult.BackupFile))" } else { '' }
                if ($nodeResult.Success) {
                    Write-Verbose " $($nodeResult.Node): SUCCESS$backupInfo" -Verbose
                    $successCount++
                } else {
                    Write-Verbose " $($nodeResult.Node): FAILED - $($nodeResult.Error)$backupInfo" -Verbose
                    $failCount++
                }
            }
            Write-Verbose "Cluster restore complete: $successCount succeeded, $failCount failed out of $($Nodes.Count) nodes." -Verbose
            if ($failCount -gt 0) {
                Write-Warning "$failCount node(s) failed. Review the output above for details."
            }
            return $remoteResults
        }

        # ---------------------------------------------------------------
        # Node scope: auto-discover latest backup if no path provided
        # ---------------------------------------------------------------
        if ([string]::IsNullOrEmpty($PageFileSettingsFilePath)) {
            $BackupDir = 'C:\ProgramData\AzStackHci.DiagnosticSettings'
            $LatestFile = Get-ChildItem "$BackupDir\PageFile_Settings_*.json" -ErrorAction SilentlyContinue |
                Sort-Object LastWriteTime -Descending | Select-Object -First 1
            if (-not $LatestFile) {
                Write-Warning "No PageFile backup files found in '$BackupDir' on $env:COMPUTERNAME."
                throw "No PageFile backup files found in '$BackupDir'. Run Get-AzStackHciPageFileSettings first to create a backup."
            }
            $PageFileSettingsFilePath = $LatestFile.FullName
            Write-Verbose "Auto-discovered latest backup file: $PageFileSettingsFilePath (Last modified: $($LatestFile.LastWriteTime))" -Verbose
        }

        # Validate backup file path
        if ($PageFileSettingsFilePath -notlike "C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_*") {
            throw "Error: Page File settings backup file path must start with 'C:\ProgramData\AzStackHci.DiagnosticSettings\PageFile_Settings_'"
        }

        # Read and parse the JSON backup file
        Write-Verbose "Reading Page File settings from backup file: $PageFileSettingsFilePath" -Verbose
        $backup = Get-Content $PageFileSettingsFilePath -Raw -ErrorAction Stop | ConvertFrom-Json

        try {
            # Get current page file settings
            [bool]$script:PageFileAutoManaged = (Get-CimInstance Win32_ComputerSystem).AutomaticManagedPagefile
        } catch {
            throw "Failed to get current page file settings. Error: $($_.Exception.Message)"
        }

        # ShouldProcess confirmation before restoring
        if (-not $PSCmdlet.ShouldProcess("Page file settings on $env:COMPUTERNAME", "Restore page file settings from '$PageFileSettingsFilePath' (saved on $($backup.BackupDate))")) {
            return
        }

        [bool]$RestorePageFileAutoManaged = $backup.PageFileAutoManaged

        if ($RestorePageFileAutoManaged) {
            # Backup had auto-managed: restore to auto-managed
            $nameValue = $backup.PageFileUsage.Name
            if (-not $nameValue) { throw 'Could not find page file Name in backup file' }

            if ($script:PageFileAutoManaged) {
                throw "Restore Page File settings: Page File is already automatically managed, no action required."
            }

            try {
                # Remove existing manual page file
                $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting
                if (($script:PageFileConfiguration | Measure-Object).Count -eq 1) {
                    $script:PageFileConfiguration | Remove-CimInstance -ErrorAction Stop
                    Write-Verbose "Existing manual page file settings removed." -Verbose
                } elseif (($script:PageFileConfiguration | Measure-Object).Count -gt 1) {
                    throw "Unexpected: $(($script:PageFileConfiguration | Measure-Object).Count) page files configured"
                }

                # Create system-managed page file
                $RestorePageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $nameValue } -ErrorAction Stop
                $RestorePageFile | Set-CimInstance -Property @{ InitialSize = 0; MaximumSize = 0 } -ErrorAction Stop

                # Enable automatic management
                $ComputerSystem = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop
                $ComputerSystem | Set-CimInstance -Property @{ AutomaticManagedPagefile = $true } -ErrorAction Stop
            } catch {
                throw "Failed to restore auto-managed page file settings. Error: $($_.Exception.Message)"
            }

            Write-Verbose "Page File settings reverted to automatically managed." -Verbose
            Write-Verbose "A system restart is required for the Page File changes to take effect." -Verbose
            return [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'RestoreAutoManaged'; BackupFile = $PageFileSettingsFilePath; Error = $null }

        } else {
            # Backup had manual config: restore with backup's settings
            $restoreName = $backup.PageFileConfig.Name
            $restoreInitial = [int]$backup.PageFileConfig.InitialSize
            $restoreMax = [int]$backup.PageFileConfig.MaximumSize
            if (-not $restoreName -or $null -eq $restoreInitial -or $null -eq $restoreMax) {
                throw 'Could not parse page file Name, InitialSize, or MaximumSize from backup file'
            }

            try {
                # Remove existing page file
                $script:PageFileConfiguration = Get-CimInstance -ClassName Win32_PageFileSetting
                if (($script:PageFileConfiguration | Measure-Object).Count -eq 1) {
                    $script:PageFileConfiguration | Remove-CimInstance -ErrorAction Stop
                    Write-Verbose "Existing page file settings removed." -Verbose
                } elseif (($script:PageFileConfiguration | Measure-Object).Count -gt 1) {
                    throw "Unexpected: $(($script:PageFileConfiguration | Measure-Object).Count) page files configured"
                }

                # Restore with backup settings
                $RestorePageFile = New-CimInstance -ClassName Win32_PageFileSetting -Property @{ Name = $restoreName } -ErrorAction Stop
                $RestorePageFile | Set-CimInstance -Property @{ InitialSize = $restoreInitial; MaximumSize = $restoreMax } -ErrorAction Stop
            } catch {
                throw "Failed to restore manual page file settings. Error: $($_.Exception.Message)"
            }

            Write-Verbose "Page File configured with InitialSize = $restoreInitial and MaximumSize = $restoreMax" -Verbose
            Write-Verbose "A system restart is required for the Page File changes to take effect." -Verbose
            return [PSCustomObject]@{ Node = $env:COMPUTERNAME; Success = $true; Action = 'RestoreManual'; BackupFile = $PageFileSettingsFilePath; Error = $null }
        }

    } # End of process block

    end {
        if ($NoOutput.IsPresent) { $script:SilentMode = $false }
        Write-Debug "Completed Restore-AzStackHciPageFileSettings function"
    }

} # End of Restore-AzStackHciPageFileSettings function

# SIG # Begin signature block
# MIIncQYJKoZIhvcNAQcCoIInYjCCJ14CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB9tbyCRMkXzT1y
# jVAKvdb+ZVMgucYJzBWrueLXsnxdW6CCDMkwggYEMIID7KADAgECAhMzAAACHPrN
# xZvoL37EAAAAAAIcMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD
# b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQxWhcNMjcwNDE1MTg1
# OTQxWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD
# VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB
# DwAwggEKAoIBAQDVsZfgOKmM31HPfoWOoNEiw0SlCiIxUMC0I9NMWbucKOw/e9lP
# oAoehQVu6SG65V4EPzrYsnBnFPNoi4/HoOdjhz1qkrEt4I6tEcxXU6oOeY9zGveC
# /3iBeuhLYxM3M/PkcUoebF+Nednm8OkdSPoDu8imViHPQq/8CQUu0WRR4rE+dMRf
# rpVqfmNi2qWCX94T4MsepijGVkwE//tJg0ryAiYdHT34LSnlG/RSBZmQRGWZ5g8j
# qnKjRParSqMft1gvjuUTVgtWNZfgcLFSK5Wa0myrq8OPcgTGGsRgun+tnSS+IxDT
# xVsAPH1OzvPjwomguByhUe/OcvUN0D5Wmp7xAgMBAAGjggGqMIIBpjAOBgNVHQ8B
# Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O
# BBYEFNoH7a2YDjOSwpkp6DHcmUS7J+0yMFQGA1UdEQRNMEukSTBHMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxFjAUBgNVBAUT
# DTIzMDAxMis1MDc1NjkwHwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEw
# YAYDVR0fBFkwVzBVoFOgUYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w
# cy9jcmwvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy
# bDBtBggrBgEFBQcBAQRhMF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9z
# b2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmcl
# MjBQQ0ElMjAyMDI0LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IC
# AQAUnEqhaRXe0T3hIJjvdQErEkrA/7bByjn6t5IArODkkRjzkYwtKMc2yYj2quaN
# rLutWw2YZcngKPy1b71YyDJQTy4NDRwaSh9Tw5thrk3NmcPrAHia5vtcBJ1CgtKK
# 7mQbIcQ22d/N3813ayCDDFewu1+jsZmX+r/aTEqaOM4TVxVtRSkuCy8nAXKuChOK
# Li/zA4XuH8iEYqIsj2YoNaeSxVmeGiERXpKdo3dDmYi0kO5w2D8VS4c3+9h6gElY
# BaAAg/dYErBg27qT3vv0zRDJhJufvCNylA8S7/+8H5E/PV5cng6na9VV/w9OV3qu
# uND6zdGa2EX38Glp50F9AIQk3p2xXmcvorDeM4XJ7UlWYBi6g80J1SSOQnInCYFE
# msfUNn3+1AaTJKSJL83quKArTac2pKhu0Yzzzrzo6HrsRiQKzpnRBb1/dMa6P3hz
# 75XbMRBctNsFhZC07WCmjExdLg2eHW5uV0TY8D5+6wozJf7vF3+WHkYPO85Z+BC6
# U4FkNbYNycZ9cE4j1tXRdyDCfml6c0HWPHjNVDObrv9lKt3qUqFpX38VCqVCyNOO
# 1UcXfQiVjJw32U2WUKZjt/neJKHEBsm9kFsLuWzkQ53+qcaSaytmsCnk2gOglrlD
# 5d3kKyvvAw+rzm0lT8K38P6PLxfZQHhu4W8dV7Av8N2ZmDCCBr0wggSloAMCAQIC
# EzMAAAA5O7Y3Gb8GHWcAAAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBS
# b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoX
# DTM2MDMyMjIyMTMwNFowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQ
# Q0EgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeq
# lRYHNa265v4IY9fH8TKhemHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo
# 0dtS/EW6I/yEL/bLSY8hKpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATv
# QVL4tcf03aTycsz8QeCdM0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a
# 1uv1zerOYMnsneRRwCbpyW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1
# FyQfK0fVkaya8SmVHQ/tOf23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfO
# GSWHIIV4YrTJTT6PNty5REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7
# ttOu1bVnXfHaqPYl2rPs20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJ
# uz2MXMCt7iw7lFPG9LXKGjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxS
# CwyoGIq0PhaA7Y+VPct5pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOm
# VQop36wUVUYklUy++vDWeEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3
# SkE/xIkgpfl22MM1itkZ35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8E
# BAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPX
# LQaUEggxMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMB
# Af8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBP
# oE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAw
# TgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv
# TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOC
# AgEAFJQfOChP7onn6fLIMKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D
# 5W4wMwYeLystcEqfkjz4NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBY
# nbu0+THSuVHTe0VTTPVhily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSI
# vgn0JksVBVMYVI5QFu/qhnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6
# aR9y34aiM1qmxaxBi6OUnyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4w
# PKC5OmHm1DQIt/MNokbbH3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7
# RTX8AdBPo0I6OEojf39zuFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK
# /fg8B2qjW88MT/WF5V5uvZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSK
# YBv0VisCzfxgeU+dquXW9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkw
# YTu/9dLeH2pDqeJZAABVDWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVT
# Ql0v4q8J/AUmQN5W4n101cY2L4A7GTQG1h32HHAvfQESWP0xghn+MIIZ+gIBATBu
# MFcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIc
# +s3Fm+gvfsQAAAAAAhwwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwG
# CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZI
# hvcNAQkEMSIEIEQ9ghRWoS1bqbKpK7STbJcqqAIptC9CGXc7YxHBfR/xMEIGCisG
# AQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAVRAmH3hJypzVdritCRA1
# xocsRGifqkQ2a3UtXhFDk0MfmQPgnl2FWOjywUYMBvGanmptFvrPpkpjdUgpdz2f
# tXKmJ6ib/bWypejUgjvktCva/A6OJ3VYybChk5PqWeXdEqRwcKEeqV551I+jeeE7
# NEPwL1OfRu9NmcJlkQUKItFfBRif0fOD4LaTilwtUSd0dsoXy5UM1pmzWe8/4tH0
# ltFAtaN3l+R6699UdmzrvVnvRq0jwpghW0f9lC7BwRTapS/Nx3NKGZHZ0q/9RMzK
# QfbPSyGpfvTlQmPDs0LerkiLZ0iVDUBWsi1NjaWvt8nyNqkU0bhEn5u2b825pKXO
# 7KGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCCF5gGCSqGSIb3DQEHAqCCF4kwgheF
# AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIB
# QQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCCXGVJKbQz8HM10bUsj
# 1q1lQfnFtamwXQlhmHEgI30wNwIGajWoxjXyGBMyMDI2MDcxNTE4MjU1OS42OTRa
# MASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0
# ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo1OTFBLTA1RTAtRDk0NzElMCMG
# A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEf4wggcoMIIFEKAD
# AgECAhMzAAACFI3NI0TuBt9yAAEAAAIUMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m
# dCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDgxNDE4NDgxOFoXDTI2MTExMzE4
# NDgxOFowgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTAr
# BgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUG
# A1UECxMeblNoaWVsZCBUU1MgRVNOOjU5MUEtMDVFMC1EOTQ3MSUwIwYDVQQDExxN
# aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOC
# Ag8AMIICCgKCAgEAyU+nWgCUyvfyGP1zTFkLkdgOutXcVteP/0CeXfrF/66chKl4
# /MZDCQ6E8Ur4kqgCxQvef7Lg1gfso1EWWKG6vix1VxtvO1kPGK4PZKmOeoeL68F6
# +Mw2ERPy4BL2vJKf6Lo5Z7X0xkRjtcvfM9T0HDgfHUW6z1CbgQiqrExs2NH27rWp
# UkyTYrMG6TXy39+GdMOTgXyUDiRGVHAy3EqYNw3zSWusn0zedl6a/1DbnXIcvn9F
# aHzd/96EPNBOCd2vOpS0Ck7kgkjVxwOptsWa8I+m+DA43cwlErPaId84GbdGzo3V
# oO7YhCmQIoRab0d8or5Pmyg+VMl8jeoN9SeUxVZpBI/cQ4TXXKlLDkfbzzSQriVi
# QGJGJLtKS3DTVNuBqpjXLdu2p2Yq9ODPqZCoiNBh4CB6X2iLYUSO8tmbUVLMMEeg
# bvHSLXQR88QNICjFoBBDCDydoTo9/TNkq80mO77wDM04tPdvbMmxT01GTod60JJx
# UGmMTgseghdBGjkN+D6GsUpY7ta7hP9PzLrs+Alxu46XT217bBn6EwJsAYAc9C28
# mKRUcoIZWQRb+McoZaSu2EcSzuIlAaNIQNtGlz2PF3foSeGmc/V7gCGs8AHkiKwX
# zJSPftnsH8O/R3pJw2D/2hHE3JzxH2SrLX1FdI7Drw145PkL0hbFL6MVCCkCAwEA
# AaOCAUkwggFFMB0GA1UdDgQWBBTbX/bs1cSpyTYnYuf/Mt9CPNhwGzAfBgNVHSME
# GDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1l
# LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsG
# AQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p
# Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMB
# Af8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDAN
# BgkqhkiG9w0BAQsFAAOCAgEAP3xp9D4Gu0SH9B+1JH0hswFquINaTT+RjpfEr8Um
# UOeDl4U5uV+i28/eSYXMxgem3yBZywYDyvf4qMXUvbDcllNqRyL2Rv8jSu8wclt/
# VS1+c5cVCJfM+WHvkUr+dCfUlOy9n4exCPX1L6uWwFH5eoFfqPEp3Fw30irMN2So
# nHBK3mB8vDj3D80oJKqe2tatO38yMTiREdC2HD7eVIUWL7d54UtoYxzwkJN1t7gE
# EGosgBpdmwKVYYDO1USWSNmZELglYA4LoVoGDuWbN7mD8VozYBsfkZarOyrJYlF/
# UCDZLB8XaLfrMfMyZTMCOuEuPD4zj8jy/Jt40clrIW04cvLhkhkydBzcrmC2HxeE
# 36gJsh+jzmivS9YvyiPhLkom1FP0DIFr4VlqyXHKagrtnqSF8QyEpqtQS7wS7ZzZ
# F0eZe0fsYD0J1RarbVuDxmWsq45n1vjRdontuGUdmrG2OGeKd8AtiNghfnabVBbg
# pYgcx/eLyW/n40eTbKIlsm0cseyuWvYFyOqQXjoWtL4/sUHxlWIsrjnNarNr+POk
# L8C1jGBCJuvm0UYgjhIaL+XBXavrbOtX9mrZ3y8GQDxWXn3mhqM21ZcGk83xSRqB
# 9ecfGYNRG6g65v635gSzUmBKZWWcDNzwAoxsgEjTFXz6ahfyrBLqshrjJXPKfO+9
# Ar8wggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEB
# CwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYD
# VQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAe
# Fw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0
# YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGm
# TOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/H
# ZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDc
# wUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62A
# W36MEBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1w
# jjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCG
# MFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ
# 1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP
# 8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFz
# ymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHz
# NgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3
# xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsG
# AQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/
# LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEG
# DCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYB
# BQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8G
# A1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQw
# VgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9j
# cmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUF
# BwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br
# aS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQEL
# BQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfC
# cTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AF
# vonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l
# 9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn
# 8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5m
# O0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyx
# TkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4
# S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9
# y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM
# +Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhw
# RNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDWTCCAkEC
# AQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0
# ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo1OTFBLTA1RTAtRDk0NzElMCMG
# A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIa
# AxUA2RysX196RXLTwA/P8RFWdUTpUsaggYMwgYCkfjB8MQswCQYDVQQGEwJVUzET
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T
# dGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsFAAIFAO4Bw8EwIhgPMjAyNjA3MTUw
# ODMwNTdaGA8yMDI2MDcxNjA4MzA1N1owdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA
# 7gHDwQIBADAKAgEAAgImoAIB/zAHAgEAAgIV4zAKAgUA7gMVQQIBADA2BgorBgEE
# AYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYag
# MA0GCSqGSIb3DQEBCwUAA4IBAQCEMQUzEDlCmzGdxBJJble+/JPLmWG7brLDpJck
# UOZz6QpHwoMJkIWrfcRt862NxlRibIF88+G0r/kZkjRVm2tg5F7PJ/rToPZcNynp
# u2AuqhSVOYN2e3RN1v70Etx1ndob+UYm4C+oC+oUFg0FCEmrq8FAVeUvgcOfFtiW
# Rn7/oS2vTDzZXqqaf98JqySDsk6HrQVSE2R8jRAd2fGv+RXWioQ5m6cLrjK+G0qR
# tYALrahgdyGi4i6rufbpDl5pnW3CfEnHvDDE60Ed69KsLK+LEbvythCtPaY18YFp
# anBEoDg49VHgTt+yTGiri9hjZcewQPao2KHJOUZixvdE9lOIMYIEDTCCBAkCAQEw
# gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAIUjc0jRO4G33IA
# AQAAAhQwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B
# CRABBDAvBgkqhkiG9w0BCQQxIgQglTaqcsqPqsVlJwCcOYR6vUby7FKhcp/xZFJa
# Bo9WF/UwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCA2eKvvWx5bcoi43bRO
# 3+EttQUCvyeD2dbXy/6+0xK+xzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwAhMzAAACFI3NI0TuBt9yAAEAAAIUMCIEIIhob0bxl4LT0M/FPkK2
# Yf3IYCDIvFfGU2IB3UJfxSMhMA0GCSqGSIb3DQEBCwUABIICAGpDfYI9uEG2/Zmj
# K2gvwWFPllRADqHowebwf9pTWOY6JUZCSLFAWzdO35o82BVLdMB+59MASvzImCNW
# dCmqqAA2UxFxi0CgVD77qIToZTYup87MLewUYMD5Ld/quy96CDjgC8dtiwzWpt5N
# zYv8hGytOHnbVv/UEn2jELplOCugtgk1bzw/8WN9901MdwtlWdcHuSvYdha6AOuu
# +c21ZkAaT1l9LOHrTj/P8Q5RDLBdmE5yLxZmowm3ECFdOmBlDbshuN/v1fHIsngl
# K14vji3tW4guJQtou1INHsaPOARtWyVwDhkEttR8qOyT8esdwsPUIg1T+zUcX2S7
# kj7xlWAXiijIcr7x8dFpr1iHbYGeZGVUtgE3j9B9fFAZotU4/zMLlKzSwN5IXfKu
# 8zGq53r2msK4pXbHK9YJeNKHA6YEGKm4N5YXKR4KCSAIVrMSiKLkw5+ovj7xdwzS
# yhkG1M7SZrd9fcmfpsGRzTlWlGKuqMERx4+KcaeKTrz/BNxuU64VGR7Og1c9abGN
# SH3Ht0qQdWttUT3+HvMV8Fre5atTnRXoNEIA34PcqQmG7yQ4bSJY32vY1qi9tnW0
# DH55yTXAS8riiyVmtwr0VUvbFbuzwpwUnCvsQQo70DlmvnXlQLQy7INiZVwL9PqA
# yktC0dRgFsB2k5G2RKD4AIAyCXy9
# SIG # End signature block