Skip to content

Instantly share code, notes, and snippets.

@DamagedDingo
Last active September 22, 2024 11:20
Show Gist options
  • Save DamagedDingo/c1b040ac71ba279b01e4f7e54d7f7c2a to your computer and use it in GitHub Desktop.
Save DamagedDingo/c1b040ac71ba279b01e4f7e54d7f7c2a to your computer and use it in GitHub Desktop.
Collect Windows Update Settings from a Client Device
<#
.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