Get Windows Update client configuration

One of the first things I do when provsioning a new server is:

  • configure the network card, assign an IP address
  • define a proxy
  • configure Windows Update settings
  • Download and install all security udpates

Every IT-pro has his own list. The Microsoft PowerShell MVP Jeffery Hicks says for example on this page: Windows Server 2012: First Five Fixes

I assume you will do the following tasks by default when setting up a new Windows Server 2012 system:
* Configure computer name
* Configure networking
* Install features and roles
* Run Windows Update
I’m not going to cover those as I think they are pretty self-evident in the Server Manager GUI, but I do have 5 additional “fixes” that I’ve been using.

He’s right for a GUI environment but it isn’t very straight forward for the Core Edition or when you’ve to automate it.

I’ve already presented how to get and set a proxy server using powershell on this page: https://p0w3rsh3ll.wordpress.com/2012/10/07/getsetclear-proxy/

Recently the Microsoft MVP Jan Egil Ring presented on the Hey Scripting guy’s blog how to Use PowerShell to Configure the NIC on Windows Server 2012

For listing and configuring Windows updates settings, currently I only know the excellent work done by Boe Prox for corporate environments. Here are some links to the great resources Boe shared and still maintains:

But this isn’t exactly what I was looking for, espcially for Non–Active Directory environments. I also wanted a console output where I don’t need to know that AUoptions represents the behavior of Automatic udpates notifications and that a value of 4 means that it should “install updates automatically”.

I didn’t want a script for Managing Windows Update with PowerShell that doesn’t take into account group policy (GPO) settings.

Even the module written by Michal Gajda and presented by Ed Wilson in the following Hey scripting guy’s blog post didn’t propose a solution to read the Windows update settings.

So I’ve been using the following resources to write my own function.

I also wanted the function to work on a freshly installed computer where Windows Updates settings haven’t been configured yet.

Function Get-WUSettings {            
[cmdletbinding()]            
Param(            
[switch]$viaRegistry=$false            
)            
Begin {            
    # Get the Operating system            
    $OSVersion = [environment]::OSVersion.Version            
            
    # Initialize object            
    $WshShell = New-Object -ComObject Wscript.Shell            
            
    $polkey = 'HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU'            
    $stdkey = 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update'            
}            
Process {            
    if ($viaRegistry) {            
        try {            
            $AUEnabled = $WshShell.RegRead("$polkey\NoAutoUpdate")            
        } catch {            
            # if this value is absent, it means it's turned on            
            $AUEnabled = 0            
        }            
        Switch ($AUEnabled) {            
            1 {$AUEnabled = $false}            
            0 {$AUEnabled = $true }            
        }            
        try {            
            $AUOptions = $WshShell.RegRead("$polkey\AUOptions")            
        } catch {            
            try {            
                $AUOptions = $WshShell.RegRead("$stdkey\AUOptions")            
            } catch {            
                $AUOptions = 0            
            }            
        }            
        Switch ($AUOptions) {            
            0 {$AUNotificationLevel = 'Not Configured'}            
            1 {$AUNotificationLevel = 'Never check for updates'}            
            2 {$AUNotificationLevel = 'Notify Before Download'}            
            3 {$AUNotificationLevel = 'Notify Before Installation'}            
            4 {$AUNotificationLevel = 'Install updates automatically'}            
        }            
        try {            
            $IncludeRecommendedUpdates = $WshShell.RegRead("$polkey\IncludeRecommendedUpdates")            
        } catch {            
            # if the value is absent we get it from            
            $IncludeRecommendedUpdates = $WshShell.RegRead("$stdkey\IncludeRecommendedUpdates")            
        }            
        Switch ($IncludeRecommendedUpdates) {            
            0 {$GetRecommendedUpdates = $false}            
            1 {$GetRecommendedUpdates = $true}            
        }            
       try {            
            $UseWUServerVal = $WshShell.RegRead("$polkey\UseWUServer")            
        } catch {            
            # if the value doesn't exist, it means that we don't use a WSUS server            
            $UseWUServerVal = 0            
        }            
        Switch ($UseWUServerVal) {            
            1 {$UseWUServer = $true}            
            0 {$UseWUServer = $false }            
        }            
        # Create a default object with a subset of properties            
        $obj = New-Object -TypeName psobject -Property @{            
            'Is Automatic Update Enabled' = $AUEnabled            
            'Use a WSUS Server' = $UseWUServer            
            'Automatic Updates Notification' = $AUNotificationLevel;            
            'Receive recommended udpates' = $GetRecommendedUpdates;            
        }            
        if ($OSVersion -lt [version]'6.2') {            
            try {            
                $ScheduledInstallDay  = $WshShell.RegRead("$polkey\ScheduledInstallDay")            
                $ScheduledInstallTime = $WshShell.RegRead("$polkey\ScheduledInstallTime")            
            } catch {            
                try {            
                    $ScheduledInstallDay  = $WshShell.RegRead("$stdkey\ScheduledInstallDay")            
                    $ScheduledInstallTime = $WshShell.RegRead("$stdkey\ScheduledInstallTime")            
                } catch {            
                    # Absent = Every Day @3 AM but I prefer to leave it blank in the returned object            
                }            
            }            
            Switch ($ScheduledInstallDay) {            
                0 {$InstallDay = 'Every Day'}            
                1 {$InstallDay = 'Every Sunday'}            
                2 {$InstallDay = 'Every Monday'}            
                3 {$InstallDay = 'Every Tuesday'}            
                4 {$InstallDay = 'Every Wednesday'}            
                5 {$InstallDay = 'Every Thursday'}            
                6 {$InstallDay = 'Every Friday'}            
                7 {$InstallDay = 'Every Saturday'}            
            }            
            if ($ScheduledInstallTime) {            
                $InstallTime = New-TimeSpan -Hours $ScheduledInstallTime            
            }            
            $obj | Add-Member -MemberType NoteProperty -Name 'Install Frequency' -Value $InstallDay            
            $obj | Add-Member -MemberType NoteProperty -Name 'Install Time' -Value $InstallTime            
        } else {            
            # These properties don't exist anymore on Windows 8            
        }            
        # Add extra properties            
        if ($UseWUServer) {            
            try {            
                $WUServer = $WshShell.RegRead('HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\WUServer')            
                $WUStatusServer =  $WshShell.RegRead('HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\WUStatusServer')            
            } catch {            
                # we silently fail            
            }            
            $obj | Add-Member -MemberType NoteProperty -Name 'WSUS Server' -Value $WUServer            
            $obj | Add-Member -MemberType NoteProperty -Name 'WSUS Status URL' -Value $WUStatusServer            
        }            
        try {            
            $OptinGUID = $WshShell.RegRead('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\DefaultService')            
        } catch {            
            # Fail silently            
        }            
        if ($OptinGUID -eq '7971f918-a847-4430-9279-4a52d1efe18d') {            
            $obj | Add-Member -MemberType NoteProperty -Name "Opted-in Microsoft Update" -Value $true            
        } else {            
            $obj | Add-Member -MemberType NoteProperty -Name "Opted-in Microsoft Update" -Value $false            
        }            
        # Return our object            
        $obj            
            
    } else {            
        # We use Com Object            
        $COMWUSettings = (New-Object -ComObject Microsoft.Update.AutoUpdate).Settings            
        # Settings might be controlled by GPO            
        if ($COMWUSettings.ReadOnly) {            
            # Use the registry            
            Get-WUSettings -viaRegistry:$true            
            break            
        } else {            
            $UseWUServer = $false            
        }            
        Switch ($COMWUSettings.NotificationLevel) {            
            0 {$AUNotificationLevel = 'Not Configured'}            
            1 {$AUNotificationLevel = 'Never check for updates'}            
            2 {$AUNotificationLevel = 'Notify Before Download'}            
            3 {$AUNotificationLevel = 'Notify Before Installation'}            
            4 {$AUNotificationLevel = 'Install updates automatically'}            
        }            
        $isAUenabled = (New-Object -ComObject Microsoft.Update.AutoUpdate).serviceEnabled            
        $obj = New-Object -TypeName psobject -Property @{            
            'Is Automatic Update Enabled' = $isAUenabled            
            'Automatic Updates Notification' = $AUNotificationLevel;            
            'Use a WSUS Server' = $UseWUServer            
            'Receive recommended udpates' = $COMWUSettings.IncludeRecommendedUpdates;            
        }            
        if ($OSVersion -lt [version]'6.2') {            
            Switch ($COMWUSettings.ScheduledInstallationDay) {            
                0 {$InstallDay = 'Every Day'}            
                1 {$InstallDay = 'Every Sunday'}            
                2 {$InstallDay = 'Every Monday'}            
                3 {$InstallDay = 'Every Tuesday'}            
                4 {$InstallDay = 'Every Wednesday'}            
                5 {$InstallDay = 'Every Thursday'}            
                6 {$InstallDay = 'Every Friday'}            
                7 {$InstallDay = 'Every Saturday'}            
            }            
            if ($COMWUSettings.ScheduledInstallationTime) {            
                $InstallTime = New-TimeSpan -Hours $COMWUSettings.ScheduledInstallationTime            
            }            
            $obj | Add-Member -MemberType NoteProperty -Name 'Install Frequency' -Value $InstallDay            
            $obj | Add-Member -MemberType NoteProperty -Name 'Install Time' -Value $InstallTime            
            
        } else {            
            # not available on W8            
        }            
        (New-Object -ComObject Microsoft.Update.ServiceManager).services | ForEach-Object {            
            if ($_.IsDefaultAUService) {            
                $OptinGUID = $_.ServiceID             
            }            
        }            
        if ($OptinGUID -eq '7971f918-a847-4430-9279-4a52d1efe18d') {            
            $obj | Add-Member -MemberType NoteProperty -Name "Opted-in Microsoft Update" -Value $true            
        } else {            
            $obj | Add-Member -MemberType NoteProperty -Name "Opted-in Microsoft Update" -Value $false            
        }            
        # return            
        $obj            
    }            
}            
End {}            
}

Here’s what I get on the Windows 7 computer I use at home:
Get-WUsettings output on W7 @home
Here’s what I get on the Windows 8 computer I use at home:
Get-WUSettings output on W8 @home

Advertisements

19 thoughts on “Get Windows Update client configuration

  1. Hi There – Is tehre anyway to get this to accept pipelined input? This fills a big need we have perfectly – except for that one small part…I need this run against groups of servers… Thanks in advance!

    • Yes, there is but for what parameter? For a ComputerName parameter to target a remote computer?
      Actually, there’s a more simple way to achieve this w/o modifying the code.
      Copy/paste the code into a .ps1 file, get rid of the function statement by removing the first and last line of the code.

      $c = Get-Credential
      Get-Content myremotetargets.txt | % {
      Invoke-Command -ComputerName $_ -File mycode.ps1 -Credential $c
      }
      
  2. Hi,
    you can help me with the following…?

    When run the function or the script among server groups the output not show something values.

    Remote Servers Groups by: Invoke-Command -ComputerName $Servers -FilePath .\Get-WUSettings.ps1

    Receive recommended udpates :
    Is Automatic Update Enabled :
    Automatic Updates Notification :
    Use a WSUS Server : False
    Opted-in Microsoft Update : False
    PSComputerName : SRVDC01
    RunspaceId : 1dffc724-fa5b-4451-be4d-755d68ca136a

    Receive recommended udpates :
    Is Automatic Update Enabled :
    Automatic Updates Notification :
    Use a WSUS Server : False
    Opted-in Microsoft Update : False
    PSComputerName : SRVFILE01
    RunspaceId : bced6e95-a1a2-43a3-84fe-17e838a93df6

    Locally on server SRVDC01: Get-WUSettings

    Receive recommended udpates : True
    Is Automatic Update Enabled : True
    Automatic Updates Notification : Notify Before Installation
    Use a WSUS Server : False
    Opted-in Microsoft Update : False

    Thanks!!

    • Hi,

      Yes, I can help 🙂

      I was able to reproduce the behavior you mentioned.

      Running the comObject through remoting doesn’t work very well.

      To override this shortcoming, you can create a file Get-WUSettings.ps1, copy/paste the above function and add the following line after the function:

      # Force reading the settings via the registry
      Get-WUSettings -viaregistry:$true
      

      And then you use the file the way you did with the invoke-command cmdlet.

  3. Hello,

    I know it has been a few years, since you published this, however, I would appreciate some help. I’m trying to ruin the script but I get nothing in return, the prompt comes back expecting another command.

    O’m executing the script as depicted in the examples in the images.

    • Hi,
      Yes, I may help.
      On what version on Windows do you run the script ? Is it Windows 10 or Server 2016 ?
      Currently there’s a hardcoded value that limits it because I wrote the script when these OS weren’t released yet.

      • I got nothing, below is a snipet of the results

        PS C:\utils\scripts> .\Get-WUSettings.ps1 -viaregistry:$true

        PS C:\utils\scripts>

      • Oh, I see you copy/pasted the above function into a file Get-WUSettings.ps1.
        If, yes, try the following:

        # import the function by dot sourcing the script
        . .\Get-WUSettings.ps1
        # Now, call/execute the imported function and not the script
        Get-WUSettings -viaregistry:$true
        

        Does this make a difference?

    • Hi,
      Copy/paste the code into a .ps1 file, get rid of the function statement by removing the first and last line of the code and do:

      $c = Get-Credential
      Get-Content myremotetargets.txt | % {
      Invoke-Command -ComputerName $_ -File mycode.ps1 -Credential $c
      } | Export-CSV -Path myoutput.csv
      
  4. I’m not sure I will have any luck but I’ll ask anyway :). I am not able to use invoke-command to run this against multiple remote servers. The environment doesn’t allow it. Is there some way I can just get the Windows Update Settings which is all I’m looking for on remote systems without invoke-command? I usually have to run wmi calls. There are other scripts out there that can pull back information but yours is more readable to my manager who is requesting this information.

    • Invoke-Command uses Powershell remoting. If it doesn’t work, it means remoting is not enabled on the targeted endpoint/server/node/workstation. If you’re in a domain environment, remoting uses by default kerberos for authentication and any data carried by PSRemoting over the network is encrypted. While WMI works fine, it’s now considered legacy (it uses DCOM) while PS remoting is considered as a modern and a more secure remote management method.

      It’s possible to rewrite the part of the function that reads the registry and make it do it using wmi calls. It would be feasible but ugly.

      You may deliver more value and have (all future) management tasks done faster, easier and in a more secure way, if you first have PSRemoting enabled everywhere and configured correctly (btw, “enabled” doesn’t mean it has to accept any incoming connection). Address first this major management issue and convince your boss that this should be a top priority for both of you.

  5. Ok thank you and I fully agree and have made the argument. Time I try again. BTW, this is pretty awesome tyvm for the work and response.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s