Last active
September 22, 2024 11:20
-
-
Save DamagedDingo/c1b040ac71ba279b01e4f7e54d7f7c2a to your computer and use it in GitHub Desktop.
Collect Windows Update Settings from a Client Device
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Retrieves Windows Update settings from the "current" and "default" registry paths for device update policies. | |
.DESCRIPTION | |
This function compares current Windows Update settings (from the current device registry path) with their default values (from the default device registry path). It displays the differences and optionally filters only configured settings in the policy. Empty or blank default values are replaced with a hyphen ("-") for better visibility. The settings are sorted alphabetically. | |
.PARAMETER IncludeProviderKeys | |
Includes keys with "_ProviderSet" and "_WinningProvider" suffixes in the output. These keys are usually omitted unless this parameter is provided. | |
.PARAMETER Mode | |
Specifies the output mode: | |
- ConfiguredByPolicyOnly (default): Displays only settings configured in the policy. | |
- AvailableandConfiguredInPolicy: Displays both configured and available settings. | |
- ConfiguredByPolicyAndOptionalSettings: Displays all configured and not configured settings. | |
.PARAMETER ExportToCsv | |
If provided, exports the output to a CSV file. Console output will be suppressed. | |
.PARAMETER OutputPath | |
Specifies the path for saving the CSV file if ExportToCsv is used. Default is "WindowsUpdateSettings.csv". | |
.EXAMPLE | |
Get-WindowsUpdateSettings -Mode ConfiguredByPolicyOnly | |
.EXAMPLE | |
Get-WindowsUpdateSettings -Mode AvailableandConfiguredInPolicy | |
.EXAMPLE | |
Get-WindowsUpdateSettings -Mode ConfiguredByPolicyAndOptionalSettings -ExportToCsv -OutputPath "C:\Reports\UpdateSettings.csv" | |
.NOTES | |
Default values are replaced with a hyphen ("-") if they are empty or blank. | |
#> | |
function Get-WindowsUpdateSettings { | |
[CmdletBinding()] | |
param ( | |
[switch]$IncludeProviderKeys, | |
[ValidateSet("ConfiguredByPolicyOnly", "AvailableandConfiguredInPolicy", "ConfiguredByPolicyAndOptionalSettings")] | |
[string]$Mode = "ConfiguredByPolicyOnly", | |
[switch]$ExportToCsv, | |
[string]$OutputPath = "WindowsUpdateSettings.csv" | |
) | |
#region Script Variables | |
$CurrentRegPath = 'HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device\Update' | |
$DefaultRegPath = 'HKLM:\SOFTWARE\Microsoft\PolicyManager\default\Update' | |
$ComparisonResults = @() | |
$IgnoredKeys = @('_ProviderSet', '_WinningProvider') | |
# Function to get registry values from the default path (fetch from subkeys) | |
Function Get-DefaultRegistryValues { | |
param ( | |
[string]$RegPath, | |
[bool]$IncludeProviders | |
) | |
$RegValues = @{} | |
# Get all subkeys under the default path | |
$SubKeys = Get-ChildItem -Path $RegPath | |
foreach ($SubKey in $SubKeys) { | |
$SubKeyPath = Join-Path -Path $RegPath -ChildPath $SubKey.PSChildName | |
$KeyValues = Get-ItemProperty -Path $SubKeyPath | |
# Handle empty or blank values as hyphen | |
if ($KeyValues.PSObject.Properties['value'] -and $KeyValues.value -ne '') { | |
$RegValues[$SubKey.PSChildName] = $KeyValues.value | |
} else { | |
$RegValues[$SubKey.PSChildName] = '-' | |
} | |
} | |
return $RegValues | |
} | |
# Function to get registry values from the current path (direct dword values) | |
Function Get-CurrentRegistryValues { | |
param ( | |
[string]$RegPath, | |
[bool]$IncludeProviders | |
) | |
$RegItems = Get-ItemProperty -Path $RegPath | Select-Object -Property * -ExcludeProperty PSPath, PSParentPath, PSChildName, PSDrive, PSProvider | |
$RegValues = @{} | |
foreach ($Item in $RegItems.PSObject.Properties) { | |
$KeyName = $Item.Name | |
# Ignore _ProviderSet or _WinningProvider keys if the switch is not set | |
if (-not $IncludeProviders -and ($KeyName -like '*_ProviderSet*' -or $KeyName -like '*_WinningProvider*')) { | |
continue | |
} | |
$RegValues[$KeyName] = $Item.Value | |
} | |
return $RegValues | |
} | |
# Function to compare values and output results | |
Function Compare-RegistryValues { | |
param ( | |
[hashtable]$CurrentValues, | |
[hashtable]$DefaultValues | |
) | |
$Comparison = @() | |
foreach ($Key in $DefaultValues.Keys) { | |
if ($CurrentValues.ContainsKey($Key)) { | |
if ($CurrentValues[$Key] -ne $DefaultValues[$Key]) { | |
$Comparison += [pscustomobject]@{ | |
Setting = $Key | |
Default = $DefaultValues[$Key] | |
Set = $CurrentValues[$Key] | |
Status = "Changed" | |
} | |
} else { | |
$Comparison += [pscustomobject]@{ | |
Setting = $Key | |
Default = $DefaultValues[$Key] | |
Set = $CurrentValues[$Key] | |
Status = "Unchanged" | |
} | |
} | |
} else { | |
# If the setting doesn't exist in current, display a hyphen | |
$Comparison += [pscustomobject]@{ | |
Setting = $Key | |
Default = $DefaultValues[$Key] | |
Set = '-' | |
Status = "Unchanged" | |
} | |
} | |
} | |
# Sort the comparison results alphabetically by setting name | |
$Comparison | Sort-Object -Property Setting | |
} | |
# Function to print settings with dots for alignment (for console table display) | |
Function Print-TableWithDots { | |
param ( | |
[pscustomobject]$ComparisonResults | |
) | |
# Find the maximum length of the setting names for dots alignment | |
$MaxLength = ($ComparisonResults | ForEach-Object { $_.Setting.Length } | Measure-Object -Maximum).Maximum | |
# Prepare table data with dots for alignment | |
$TableData = foreach ($Result in $ComparisonResults) { | |
$DotsNeeded = $MaxLength - $Result.Setting.Length | |
$Dots = "." * $DotsNeeded | |
[pscustomobject]@{ | |
Setting = "$($Result.Setting)$Dots" | |
Set = $Result.Set | |
Default = $Result.Default | |
} | |
} | |
# Display everything normally without moving configured settings to the top | |
$TableData | Format-Table @{Label="Setting";Expression={$_.Setting};Align="Left"}, | |
@{Label="Set";Expression={$_.Set};Align="Left"}, | |
@{Label="Default";Expression={$_.Default};Align="Left"} | |
} | |
#endregion | |
#region Main Execution | |
try { | |
# Get current and default registry values (handling structure differences) | |
$CurrentValues = Get-CurrentRegistryValues -RegPath $CurrentRegPath -IncludeProviders $IncludeProviderKeys | |
$DefaultValues = Get-DefaultRegistryValues -RegPath $DefaultRegPath -IncludeProviders $IncludeProviderKeys | |
# Compare values and find the changes | |
$ComparisonResults = Compare-RegistryValues -CurrentValues $CurrentValues -DefaultValues $DefaultValues | |
# Determine which mode is being used and what data to display | |
$FilteredResults = switch ($Mode) { | |
"ConfiguredByPolicyOnly" { | |
# Show only settings configured in the policy | |
$ComparisonResults | Where-Object { $_.Set -ne '-' } | |
} | |
"AvailableandConfiguredInPolicy" { | |
# Show both configured and available settings (configured in current and available in default) | |
$ComparisonResults | Where-Object { $_.Set -ne '-' -or $_.Default -ne '-' } | |
} | |
"ConfiguredByPolicyAndOptionalSettings" { | |
# Show all settings (both configured and not configured) | |
$ComparisonResults | |
} | |
} | |
# Output to console in a table with dots for alignment | |
if (-not $ExportToCsv) { | |
Print-TableWithDots -ComparisonResults $FilteredResults | |
} | |
# Export to CSV if specified | |
if ($ExportToCsv) { | |
$FilteredResults | Select-Object Setting, Set, Default | Export-Csv -Path $OutputPath -NoTypeInformation | |
Write-Host "Exported to $OutputPath" | |
} | |
} catch { | |
Write-Error "An error occurred: $_" | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment