8

How to view and restore hidden Windows Updates with PowerShell

When you visit Windows Update, you can hide an update to avoid being prompted to install it again at next scan. If you’ve ever hidden an update, you’ve also the ability to restore hidden updates in the Windows Update control panel applet by clicking the “restore hidden updates” button. However, over time, you’ll forget what update(s) you’ve hidden. There’s no built-in way to show hidden updates.

image001

Let’s see how PowerShell can help in this situation and define 2 functions and a filter to view already hidden updates.

Function Get-WindowsUpdate {

    [Cmdletbinding()]
    Param()

    Process {
        try {
            Write-Verbose "Getting Windows Update"
            $Session = New-Object -ComObject Microsoft.Update.Session            
            $Searcher = $Session.CreateUpdateSearcher()            
            $Criteria = "IsInstalled=0 and DeploymentAction='Installation' or IsPresent=1 and DeploymentAction='Uninstallation' or IsInstalled=1 and DeploymentAction='Installation' and RebootRequired=1 or IsInstalled=0 and DeploymentAction='Uninstallation' and RebootRequired=1"            
            $SearchResult = $Searcher.Search($Criteria)           
            $SearchResult.Updates
        } catch {
            Write-Warning -Message "Failed to query Windows Update because $($_.Exception.Message)"
        }
    }
}

Function Show-WindowsUpdate {
    Get-WindowsUpdate |
    Select Title,isHidden,
        @{l='Size (MB)';e={'{0:N2}' -f ($_.MaxDownloadSize/1MB)}},
        @{l='Published';e={$_.LastDeploymentChangeTime}} |
    Sort -Property Published
}

Now to show all hidden updates, you only need to do the following:

Show-WindowsUpdate | Where { $_.isHidden }| Out-GridView

image002

To be able to restore all hidden updates, an additional advanced function is required.

Function Set-WindowsHiddenUpdate {

    [Cmdletbinding()]

    Param(
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [System.__ComObject[]]$Update,

        [Parameter(Mandatory=$true)]
        [boolean]$Hide
    )

    Process {
        $Update | ForEach-Object -Process {
            if (($_.pstypenames)[0] -eq 'System.__ComObject#{c1c2f21a-d2f4-4902-b5c6-8a081c19a890}') {
                try {
                    $_.isHidden = $Hide
                    Write-Verbose -Message "Dealing with update $($_.Title)"
                } catch {
                    Write-Warning -Message "Failed to perform action because $($_.Exception.Message)"
                }
            } else {
                Write-Warning -Message "Ignoring object submitted"
            }
        }
    }
}

Let’s first hide four random updates:

Get-WindowsUpdate | Get-Random -Count 4 |
Set-WindowsHiddenUpdate -Hide $true -Verbose

To restore all hidden updates, we can simply do:

Get-WindowsUpdate| Set-WindowsHiddenUpdate -Hide $false -Verbose

But if we want to restore only some of them, that’s also possible:

Get-WindowsUpdate | Where { $_.isHidden } | Out-GridView -PassThru |
Set-WindowsHiddenUpdate -Hide $false -Verbose

In Europe, Windows Update proposes an update called on my Windows 8.1 the Microsoft Browser Choice Screen Update for EEA Users of Windows 8.1 for x64-based Systems (KB976002)

The problem with this update is that it’s marked as a permanent component and thus cannot be uninstalled.

With the above functions, we are now able to hide this update like this:

Get-WindowsUpdate |
Where { $_.Title -match 'Microsoft Browser Choice Screen Update'} |
Set-WindowsHiddenUpdate -Hide $true -Verbose

image003

Filed in: Articles, Online Only Tags: ,

8 Responses to "How to view and restore hidden Windows Updates with PowerShell"

  1. Ken Grubbs says:

    This is very useful code. The only difficulty with the modules I have encountered is using PowerShell Remoting to establish a remote session, then attempting to hide an update. An Access denied occurs. Is there a workaround for that problem? I would like to step through a list of remote servers, hiding a specific update on each one without having to RDP into each to run the script.

    • _Emin_ says:

      Hi,

      Sorry for the late reply, I’m very busy this month…

      I’d like to help you. To be able to diagnose whether the issue is related to remoting, the above code, or both, I need to know more about your environment and how you run the code.

      If it’s not too late, could you please describe the OS levels, the PowerShell version running on both ends. Do you use a credential? Is the account running the code privileged on the client and server?

      It also depends on how the WUA (Windows Update Agent) is configured on the target machine where you want to hide the update. How is the WUA configured? Do is run normally in a GUI when you logon with admin rights? Is it configured to run against a WSUS server, Windows Update, Microsoft Update,…?

      /Emin

  2. Dave says:

    Hi,
    I realize your busy, but I figured I’d throw these questions out there while I’m looking for a solution myself.
    A few things:
    1. This script is GREAT! I’m using it to hide all the language packs and random updates that aren’t security-related, to make an MDT-built image look really clean at the end. Management likes to look at Windows Update and see it empty, implying that the computer is fully patched. As far as your script, I especially like how it hides all the language packs: I use a -like *KB2483139* with the where clause. It loops all 30+ language packs almost instantly! Other updates, one-at-a-time, are dirt slow. QUESTION: Is there any way to make it faster? I tried instantiating the com object before the function (instead of opening one every time the function is called) to hopefully shave some time off, but it didn’t help.. Not noticeably, at least.
    2. I can’t seem to hide Definition Updates for Windows Defender, or Malicious Software Removal Tools. The script says it’s doing something, but alas when I check the Windows Update GUI, they’re still listed there. QUESTION: any insight on these two particular types of updates?
    3. My organization is still on Internet Explorer 9. IE10 and IE11 are considered updates. I can hide IE11 using the script, but can’t hide IE10. I thought I might need to hide IE11, kick off a WSUS scan, then when IE10 shows up, hide it at that point. However, when I do that manually (Hide IE11 using the script, kick off a WSUS scan manually, which causes IE10 to show up, then use the script to hide IE10) it doesn’t work. It just won’t hide IE10. QUESTION: any insight on this?

    More info: the WUA is pointed towards Microsoft’s Microsoft Update Internet server. I normally run the GUI while logged on with administrative rights to the computer, as well as the PowerShell script. Most of the updates hide just fine, it’s just two types as described above, along with IE10.

    Thank you in advance for any help you can give. If you can’t, I totally understand. As I said, I’ll be working towards a solution to these issues myself. If I find anything, I’ll follow up here as well.

    • _Emin_ says:

      Hi,

      Thx for the feedback and questions.

      1. The performance issue stems from the Get-WindowsUpdate function as it performs a scan of updates. I don’t know how you hide updates but if you want to hide multiple updates, you should perform only one scan and then hide various updates. You can achieve this by capturing the result of the scan into a variable

      $allUpdates = Get-WindowsUpdate
      $allUpdates | ? Title -match "KB890830" | Set-WindowsHiddenUpdate -Hide:$true -Verbose
      

      2. Based on my tests, hiding a MSRT (Malicious Software Removal Tools) works but at next scan, the one from the previous month pops-up…I’ve hidden successfully the one from July and at next interactive scan, the one from June was proposed.
      In this case, I would recommend to use a WSUS where MSRT updates are not approved.

      3. To control IE versions proposed, you can use toolkits to block their delivery
      http://www.microsoft.com/en-us/download/details.aspx?id=179
      http://www.microsoft.com/en-us/download/details.aspx?id=36512
      http://www.microsoft.com/en-us/download/details.aspx?id=40722
      In my environment, I used both. It just sets a registry key per version.
      If you have a WSUS, you have even a better control as you can simply not approve or decline IE1x and its related cumulative updates.

      Last thing, you can talk about updates issues on the patchmanagement mailing list and there’s a WSUS mailing list as well. To subscribe,…goto http://www.patchmanagement.org/

  3. Ian says:

    Emin and Dave…
    Do you have a source for these cmdlets? Can you link to a module that I can download?
    Thanks in advance

    • Dave says:

      I’m simply using the Functions from this article. The functions are the cmdlets: Get-WindowsUpdate, Show-WindowsUpdate, Set-WindowsHiddenUpdate. Create your PS script with the functions in the beginning, and you can use the functions/cmdlets at the end of the script, after the functions.

  4. Ian says:

    D’oh!
    I should have followed the words on the page.
    It is a bit slow to process isn’t it. Still good stuff though.
    Thanks Dave

Leave a Reply

Submit Comment

© 2016 PowerShell Magazine. All rights reserved. XHTML / CSS Valid.
Proudly designed by Theme Junkie.
%d bloggers like this: