Public/AzStackHci.MemoryDumpSettings.ps1
|
# /////////////////////////////////////////////////////////////////// # Set-AzStackHciMemoryDumpSettings Main function # Used to set the registry settings required to obtain a kernel memory dump # /////////////////////////////////////////////////////////////////// Function Set-AzStackHciMemoryDumpSettings { <# .SYNOPSIS Sets the registry settings required to obtain a kernel memory dump .DESCRIPTION Sets the fourteen recommended registry settings required for the system to generate a kernel memory dump. Enabling memory dumps on the nodes with large memory size, the size of the dedicated dump file is calculated based on the system memory present in the node. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([void])] param ( # Path to drive letter for DedicatedDumpFile [Parameter(Mandatory=$true,Position=1)] [ValidateScript({Test-Path $_})] [String] [ValidatePattern('^[C-Zc-z]:\\?$')]$DedicatedDumpFileDriveLetter, # Drive letter for the dedicated dump file, must be a valid drive letter (C: through Z:) # Optional switch, used to minimize the page file size if required to save space [Parameter(Mandatory=$false,Position=2)] [Switch] $ConfigureMinimumPageFile, # Optional switch, used to ignore disk space check for the dedicated dump file [Parameter(Mandatory=$false,Position=3)] [Switch] $IgnoreDiskSpaceCheck, [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")] [switch]$NoOutput ) begin { # Requires administrator permissions to set memory dump settings in the registry if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } } process { # Handle -NoOutput: suppress all console output if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # if the drive letter is only 2 characters (C:), add a backslash to the end (C:\) if($DedicatedDumpFileDriveLetter.Length -eq 2){ $DedicatedDumpFileDriveLetter = $($DedicatedDumpFileDriveLetter + "\") } # Call function to Set the Size of the DedicateDumpFile based on node memory size [uint32]$script:DedicatedDumpFileSize = SetDedicatedDumpFileSize # Call function to Set the Path of the DedicateDumpFile and validate there is sufficient space if($IgnoreDiskSpaceCheck.IsPresent){ # Call function to Set the Path of the DedicateDumpFile and ignore disk space check [string]$script:DedicatedDumpFilePath = SetDedicatedDumpFilePath $DedicatedDumpFileDriveLetter -IgnoreDiskSpaceCheck } else { # Call function to Set the Path of the DedicateDumpFile and validate there is sufficient space [string]$script:DedicatedDumpFilePath = SetDedicatedDumpFilePath $DedicatedDumpFileDriveLetter } if($ConfigureMinimumPageFile.IsPresent){ # Call function to Set Page File to minimum 4GB settings Set-AzStackHciPageFileMinimumSettings -PageFileFileDriveLetter $DedicatedDumpFileDriveLetter } # Call function to Get and Save current page file settings Get-AzStackHciMemoryDumpSettings $HKLMCrashControl = "HKLM:\System\CurrentControlSet\Control\CrashControl" # ShouldProcess gate: prompt user before modifying registry settings if (-not $PSCmdlet.ShouldProcess("CrashControl registry ($HKLMCrashControl)", "Set kernel memory dump registry values")) { return } # Capture original registry values for rollback in case of partial failure $registryNames = @('AutoReboot','DisableEmoticon','CrashDumpEnabled','FilterPages','NMICrashDump', 'DedicatedDumpFile','DumpFileSize','IgnorePagefileSize','AlwaysKeepMemoryDump','LogEvent', 'DumpFile','MinidumpsCount','Overwrite','MinidumpDir') $originalValues = @{} foreach ($name in $registryNames) { try { $originalValues[$name] = (Get-ItemProperty -Path $HKLMCrashControl -Name $name -ErrorAction Stop).$name } catch { # Setting does not exist yet — mark for removal on rollback $originalValues[$name] = $null } } try { Set-ItemProperty -Path $HKLMCrashControl -Name AutoReboot -Type DWord -Value 1 -ErrorAction Stop # Automatic reboot Set-ItemProperty -Path $HKLMCrashControl -Name DisableEmoticon -Type DWord -Value 1 -ErrorAction Stop # Disable emoticons (display bug check error information on console) Set-ItemProperty -Path $HKLMCrashControl -Name CrashDumpEnabled -Type DWord -Value 2 -ErrorAction Stop # Kernel memory dump Set-ItemProperty -Path $HKLMCrashControl -Name FilterPages -Type DWord -Value 1 -ErrorAction Stop # Filter pages, used for active memory dump, but still set it for kernel memory dump Set-ItemProperty -Path $HKLMCrashControl -Name NMICrashDump -Type DWord -Value 1 -ErrorAction Stop # Support NMI crashes Set-ItemProperty -Path $HKLMCrashControl -Name DedicatedDumpFile -Type String -Value $DedicatedDumpFilePath -ErrorAction Stop # Dedicated Dump File Path Set-ItemProperty -Path $HKLMCrashControl -Name DumpFileSize -Type DWord -Value $DedicatedDumpFileSize -ErrorAction Stop # Dedicated Dump File size Set-ItemProperty -Path $HKLMCrashControl -Name IgnorePagefileSize -Type DWord -Value 1 -ErrorAction Stop # Required for large memory systems Set-ItemProperty -Path $HKLMCrashControl -Name AlwaysKeepMemoryDump -Type DWord -Value 1 -ErrorAction Stop # Prevents automatic deletion of memory dump files Set-ItemProperty -Path $HKLMCrashControl -Name LogEvent -Type DWord -Value 1 -ErrorAction Stop # Log event 1001 in System log # Memory dump file, should be the same drive as the dedicated dump file Set-ItemProperty -Path $HKLMCrashControl -Name DumpFile -Type ExpandString -Value "$($DedicatedDumpFileDriveLetter)memory.dmp" -ErrorAction Stop # Minidump settings Set-ItemProperty -Path $HKLMCrashControl -Name MinidumpsCount -Type DWord -Value 1 -ErrorAction Stop Set-ItemProperty -Path $HKLMCrashControl -Name Overwrite -Type DWord -Value 1 -ErrorAction Stop if(-not(test-path "C:\Windows\Minidump")) { New-Item "C:\Windows\Minidump" -ItemType Directory -ErrorAction SilentlyContinue | Out-Null } Set-ItemProperty -Path $HKLMCrashControl -Name MinidumpDir -Type ExpandString -Value "C:\Windows\Minidump\" -ErrorAction Stop } catch { # Rollback: restore original values to avoid leaving the system in a partially configured state Write-Verbose "Registry write failed, rolling back to original values. Error: $($_.Exception.Message)" -Verbose foreach ($name in $registryNames) { try { if ($null -eq $originalValues[$name]) { # Setting did not exist before — remove it if it was created Remove-ItemProperty -Path $HKLMCrashControl -Name $name -Force -ErrorAction SilentlyContinue } else { Set-ItemProperty -Path $HKLMCrashControl -Name $name -Value $originalValues[$name] -ErrorAction SilentlyContinue } } catch { Write-Warning "Rollback: Failed to restore '$name'. Manual intervention may be required." } } Throw "Failed to set CrashControl registry settings (rolled back). Error: $($_.Exception.Message)" } Write-Verbose "System memory dump settings have been configured for a Kernel Memory Dump to be written to '$($DedicatedDumpFileDriveLetter)memory.dmp'" -Verbose Write-Verbose "Dedicated Dump File configured to '$DedicatedDumpFilePath', with a size of $DedicatedDumpFileSize MiB ($([math]::round($DedicatedDumpFileSize*1MB/1Gb)) GiB).`n`n" -Verbose Write-Verbose "A system restart is required for the Memory Dump changes to take effect." -Verbose } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Set-AzStackHciMemoryDumpSettings function" } } # End of Set-AzStackHciMemoryDumpSettings # /////////////////////////////////////////////////////////////////// # Restore-AzStackHciMemoryDumpSettings function # Used to restore or roll back crash control registry settings # /////////////////////////////////////////////////////////////////// Function Restore-AzStackHciMemoryDumpSettings { <# .SYNOPSIS Restores registry values for crash control / memory dump settings .DESCRIPTION Restores the fourteen recommended registry settings from an earlier time, using a backup file as input. Requires administrator permissions to restore memory dump settings in the registry #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([void])] param ( # File path to backup file [Parameter(Mandatory=$true,Position=0)] [ValidateScript({Test-Path $_})] [String]$MemoryDumpSettingsFilePath, # File used to restore settings from, must be a valid path [Parameter(Mandatory=$false, HelpMessage="Optional switch to prevent console output from the function.")] [switch]$NoOutput ) begin { # Requires administrator permissions to set memory dump settings in the registry if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } } process { # Handle -NoOutput: suppress all console output if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # Read the memory dump settings from the backup file into an array # Resolve the path first to prevent directory traversal (e.g. '..') within the allowed prefix try { $ResolvedBackupPath = (Resolve-Path -Path $MemoryDumpSettingsFilePath -ErrorAction Stop).Path } catch { throw "Error: Unable to resolve backup file path '$MemoryDumpSettingsFilePath'. File may not exist. Error: $($_.Exception.Message)" } if($ResolvedBackupPath -like "C:\ProgramData\AzStackHci.DiagnosticSettings\MemoryDump_Settings_*"){ Write-Verbose "Reading Memory Dump settings from backup file: $ResolvedBackupPath" -Verbose } else { Write-Verbose "Memory Dump settings backup file path must resolve to 'C:\ProgramData\AzStackHci.DiagnosticSettings\MemoryDump_Settings_*'" -Verbose throw "Error: Memory Dump settings backup file path must resolve to 'C:\ProgramData\AzStackHci.DiagnosticSettings\MemoryDump_Settings_*'. Resolved path: '$ResolvedBackupPath'" } # Use the resolved path for all subsequent operations $MemoryDumpSettingsFilePath = $ResolvedBackupPath [string[]]$RestoreMemoryDumpSettings = Get-Content $MemoryDumpSettingsFilePath -ErrorAction Stop $HKLMCrashControl = "HKLM:\System\CurrentControlSet\Control\CrashControl" # Date time the Memory Dump settings were saved should be the first line in the file try { [datetime]$DateTimeMemoryDumpSettings = $RestoreMemoryDumpSettings[0] } catch { Write-Verbose "Failed to get the date and time the Memory Dump settings were saved. Error: $($_.Exception.Message)" -Verbose throw "Error: Failed to get the date and time the Memory Dump settings were saved. Error: $($_.Exception.Message)" } # Confirm with user before restoring the memory dump settings Write-Verbose "Restoring Memory Dump settings from $MemoryDumpSettingsFilePath, saved on $DateTimeMemoryDumpSettings" -Verbose if (-not $PSCmdlet.ShouldProcess("CrashControl registry ($HKLMCrashControl)", "Restore memory dump settings from backup saved on $DateTimeMemoryDumpSettings")) { return } # Restore the memory dump settings, the first six lines (0-5) are the date and time, and the next 14 lines are the settings # i = 6 is the seventh line in the file, which is the first setting for($i=6; $i -lt ($RestoreMemoryDumpSettings.Count -2); $i++) { $Setting = $RestoreMemoryDumpSettings[$i] # Debugging output, when running with -Debug Write-Debug "Backup file input: $Setting" # Split the setting into the name and value $SettingName = $Setting.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[0].Trim() $SettingValue = $Setting.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() # Check if the setting value is a comment, as these are not set in the registry if($SettingValue -eq "//Setting"){ $SettingValue = $Setting.Split("//",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() } # Check if the setting was not configured, so will need deleting to restore the settings if($SettingValue -eq "Setting not configured") { # Check if the setting is present in the registry Remove-Variable TestValue -ErrorAction SilentlyContinue try { Get-ItemProperty -Path $HKLMCrashControl -Name $SettingName -ErrorAction SilentlyContinue -ErrorVariable TestValue | Out-Null } catch { Write-Verbose "Setting '$SettingName' not present in the registry." -Verbose } # Execute the setting removal if it was not configured and is present in the registry if(-not($TestValue)) { # Value exists, remove the setting if it was not configured in the backup file Write-Verbose "Removing setting '$SettingName' from the registry." -Verbose Remove-ItemProperty -Path $HKLMCrashControl -Name $SettingName -Force -ErrorAction Stop | Out-Null } else { Write-Verbose "Info: Expected setting '$SettingName' to be configured in the registry, but is not." -Verbose } # Setting is configured still, restore the setting value from the backup file } else { # Get the current setting in the registry, to get the type of the value Remove-Variable TestValue -ErrorAction SilentlyContinue try { $RegValue = Get-ItemProperty -Path $HKLMCrashControl -Name $SettingName -ErrorAction SilentlyContinue -ErrorVariable TestValue } catch { Write-Error "Setting '$SettingName' not present in the registry." -Verbose } if(-not($TestValue)) { # Get the type of the registry value $RegValueType = (($RegValue | Get-Member | Where-Object{$_.Name -eq $SettingName}).Definition -split " ")[0] if($RegValueType -eq "int") { # Convert the registry value type to DWord $RegValueType = "DWord" } elseif($RegValueType -eq "string") { # Convert the registry value type to ExpandString $RegValueType = "ExpandString" } # Restore the registry value to match the backup file setting try { Write-Verbose "Restoring setting '$SettingName' to value '$SettingValue', type = '$RegValueType'." -Verbose Set-ItemProperty -Path $HKLMCrashControl -Name $SettingName -Type $RegValueType -Value $SettingValue -ErrorAction Stop } catch { Write-Error "Failed to restore setting '$SettingName' to value '$SettingValue'. Error: $($_.Exception.Message)" } # Error, the setting is not present in the registry! } else { throw "Expected setting '$SettingName' to be configured in the registry, but is not." } } } # End of for loop Write-Verbose "Memory dump settings have been restored from backup file." -Verbose Write-Verbose "A system restart is required for the Memory Dump changes to take effect." -Verbose } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Restore-AzStackHciMemoryDumpSettings function" } } # End of Restore-AzStackHciMemoryDumpSettings # /////////////////////////////////////////////////////////////////// # Get-AzStackHciMemoryDumpSettings function # Used to get the current memory dump settings on the system # /////////////////////////////////////////////////////////////////// Function Get-AzStackHciMemoryDumpSettings { <# .SYNOPSIS Gets current crash dump settings .DESCRIPTION Queries the registry for current crash dump settings #> [CmdletBinding()] [OutputType([void])] param ( [switch]$NoOutput ) begin { # Requires administrator permissions to get crash dump settings and write to log file to disk if (-not (Test-Elevation)) { throw "This script must be run as an Administrator." } } process { # Handle -NoOutput: suppress all console output if ($NoOutput.IsPresent) { $script:SilentMode = $true $VerbosePreference = 'SilentlyContinue' $DebugPreference = 'SilentlyContinue' } # Call function, to Set the Size of the DedicateDumpFile based on node memory size [uint32]$script:DedicatedDumpFileSize = SetDedicatedDumpFileSize $HKLMCrashControl = "HKLM:\System\CurrentControlSet\Control\CrashControl" $CrashControlSettings = Get-Item -Path $HKLMCrashControl -ErrorAction Stop | Get-ItemProperty -ErrorAction Stop $script:CurrentDumpFile = $CrashControlSettings.DumpFile $HKLMCrashControl = "HKLM:\System\CurrentControlSet\Control\CrashControl" $CrashControlProperties = "AutoReboot","DisableEmoticon","LogEvent","CrashDumpEnabled","FilterPages","NMICrashDump","DedicatedDumpFile","DumpFileSize","IgnorePagefileSize","AlwaysKeepMemoryDump","DumpFile","MinidumpsCount","Overwrite","MinidumpDir" $script:CurrentSettings = @{} # Create a hashtable to store the current settings properties foreach($PropertyName in $CrashControlProperties) { # Get-ItemPropertyValue throws even with -ErrorAction SilentlyContinue $CurrentSetting = Get-ItemProperty $HKLMCrashControl -Name $PropertyName -ErrorAction SilentlyContinue if ($CurrentSetting) { # Add description if($PropertyName -eq "CrashDumpEnabled"){ $CurrentCrashDumpMode = switch ($CurrentSetting.CrashDumpEnabled) { 1 { if ($CurrentSetting.FilterPages) { "CrashDumpEnabled = 1 (Active Memory Dump)" } else { "CrashDumpEnabled = 1 (Complete Memory Dump)" } } 2 {"CrashDumpEnabled = 2 (Kernel Memory Dump)"} 3 {"CrashDumpEnabled = 3 (Small Memory Dump)"} 7 {"CrashDumpEnabled = 7 (Automatic Memory Dump)"} default {"Unknown"} } } # Add actual configured value $CurrentSettings.$PropertyName = $CurrentSetting.$PropertyName } else { $CurrentSettings.$PropertyName = "//Setting not configured" } } # Output the current settings to the console Write-Verbose "Current Memory Dump settings:`n" -Verbose $CurrentSettings | Out-String | Write-Verbose -Verbose $DateFormatted = Get-Date -f "yyyyMMdd" if(-not(Test-Path "C:\ProgramData\AzStackHci.DiagnosticSettings\")) { New-Item "C:\ProgramData\AzStackHci.DiagnosticSettings\" -ItemType Directory -ErrorAction SilentlyContinue | Out-Null } "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss")" | Out-File $("C:\ProgramData\AzStackHci.DiagnosticSettings\MemoryDump_Settings_$DateFormatted.txt") -ErrorAction Stop "Backup of Crash Dump registry settings on $env:COMPUTERNAME - (current configuration = $CurrentCrashDumpMode)`n" | Out-File $("C:\ProgramData\AzStackHci.DiagnosticSettings\MemoryDump_Settings_$DateFormatted.txt") -Append -ErrorAction Stop $CurrentSettings | Out-File $("C:\ProgramData\AzStackHci.DiagnosticSettings\MemoryDump_Settings_$DateFormatted.txt") -Append -ErrorAction Stop Write-Verbose "Current Memory Dump settings exported to 'C:\ProgramData\AzStackHci.DiagnosticSettings\MemoryDump_Settings_$DateFormatted.txt'" -Verbose } # End of process block end { if ($NoOutput.IsPresent) { $script:SilentMode = $false } Write-Debug "Completed Get-AzStackHciMemoryDumpSettings function" } } # End of Get-AzStackHciMemoryDumpSettings function |