Although I am an engineer, I used to be responsible for approving Windows updates for the whole company. Can you imagine a more boring task? It usually took me a couple hours to approve all the updates. No wonder I looked for a more efficient way to do it.

The PowerShell script discussed in this post is my solution.

$log = "C:\Temp\Approved_Updates.txt"
$switch = "dev"
$wsusserver = "wsus" 
[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")   
$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($wsusserver,$False)
$groups = $null

if ($switch -eq "dev") 
  {
   $groups = cat "C:\Temp\wsus_devDR.txt"
  }
if ($switch -eq "prod")
  {
   $groups = cat "C:\Temp\wsus_PROD.txt"
  }
if ($switch -eq "clients")
  {
   $groups = cat "C:\Temp\Wsus_Client.txt"
  }
  

if ($groups -eq $null)   
  {
   "No groups are presented" | Out-File $log -append 
   return 0
  }

$updates = $wsus.GetUpdates() | ? {($_.CreationDate -gt "10/1/2013" -and $_.CreationDate -lt "11/1/2013" -and $_.Title -notmatch "Itanium" -and $_.Title -notmatch ".net Framework" -and $_.PublicationState -ne "Expired" ) -and ($_.ProductFamilyTitles -eq "Windows" -or $_.ProductFamilyTitles -eq "Office") -and ($_.UpdateClassificationTitle -eq "Security Updates" -or $_.UpdateClassificationTitle -eq "Critical Updates")}
foreach ($group in $groups)
  {
   $wgroup = $wsus.GetComputerTargetGroups() | where {$_.Name -eq $group}
   foreach ($update in $updates)
     {
      $update.Approve(“Install”,$wgroup)
     }  
  }
"Approved updates (on " + $date + "): " | Out-File $log -append 
"Updates have been approved for following groups: (" + $groups + ")" | Out-File $log -append 
"Following updates have been approved:" | Out-File $log -append 
$updates | Select Title,ProductTitles,KnowledgebaseArticles,CreationDate | Out-File $log -append

Let’s go through the script line by line:

$log = "C:\Temp\Approved_Updates.txt"

The $log variable stores the location of the log file. We need the log file to check what updates were approved, when, and to which WSUS groups.

$switch = "dev"

I had to approve updates in stages to three client computer types—development (dev), production (prod), and then to all clients (clients). The corresponding groups exist on my WSUS server. With the help of the $switch variable, I determine which computer group I want to target.

$wsusserver = "wsus"

Here, I set up the variable that stores the WSUS server name:

[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")   
 $wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($wsusserver,$False)

In order to manage WSUS with PowerShell, I first load the WSUS snap-in and then connect to the WSUS server.

$groups = $null

This lines import a set of computer names which I store in the $group variable.

if ($switch -eq "dev") 
   {
    $groups = cat "C:\Temp\wsus_devDR.txt"
   }
if ($switch -eq "prod")
   {
    $groups = cat "C:\Temp\wsus_PROD.txt"
   }
if ($switch -eq "clients")
   {
    $groups = cat "C:\Temp\Wsus_Client.txt"
   }

Here I check the value of the $switch variable to determine WSUS groups that I stored in text files, so I don't have to hardcode them in the script. Each line in the text file contains a WSUS group.

WSUS groups sample file

WSUS groups sample file

if ($groups -eq $null)   
   {
    "No groups are presented" | Out-File $log -append 
    return 0
   }

Here I’m checking if groups were assigned to the $groups variable. If not, the script has nothing to work with and has to exit.

$updates = $wsus.GetUpdates() | ? {($_.CreationDate -gt "6/1/2016" -and $_.CreationDate -lt "7/1/2013" -and $_.Title -notmatch "Itanium" -and $_.Title -notmatch ".net Framework" -and $_.PublicationState -ne "Expired" ) -and ($_.ProductFamilyTitles -eq "Windows" -or $_.ProductFamilyTitles -eq "Office") -and ($_.UpdateClassificationTitle -eq "Security Updates" -or $_.UpdateClassificationTitle -eq "Critical Updates")}

Using the GetUpdates() method, I get the updates I’m going to approve from WSUS and store them in the $updates variable. As you can see, I have quite a few criteria for the updates: They have to be created between 6/1/2016 and 7/1/2016, shouldn’t be updates for computers with an Itanium processor (because we don’t have such computers). .Net framework updates should not be included (because they break some of our web clients). They should not be expired; they must belong to Windows or the Office product families and must be security or critical updates.

foreach ($group in $groups)
   {
    $wgroup = $wsus.GetComputerTargetGroups() | where {$_.Name -eq $group}
    foreach ($update in $updates)
      {
       $update.Approve(“Install”,$wgroup)
      }  
   }

Now I’m looping through the groups that were stored in the $group variable. For each group name, I’m using the GetComputerTargetGroups() method to check whether the corresponding group exits in WSUS. If there is such group, I’m storing it in the $wgroup variable. Then I loop through all the updates stored in the $updates variable to approve and install them on the group stored in the $wgroup variable.

"Approved updates (on " + $date + "): " | Out-File $log -append 
 "Updates have been approved for following groups: (" + $groups + ")" | Out-File $log -append 
 "Following updates have been approved:" | Out-File $log -append 
 $updates | Select Title,ProductTitles,KnowledgebaseArticles,CreationDate | Out-File $log –append

At the end of the script, I write information about the groups and approved updates into the log file using the Out-File cmdlet. With the help of the Select-Object cmdlet, I pick the information I’m interested in. This is what the log file looks like:

Subscribe to 4sysops newsletter!

Sample log file

Sample log file

2 Comments
  1. Mark Plaga 4 years ago

    I believe that the below line should read: 

    This lines import a set of Computer Group names which I store in the $group variable. 

    or simply

    This lines import a set of  Group names which I store in the $group variable. 

    instead of,

    This lines import a set of computer names which I store in the $group variable.

     

  2. Mario 4 years ago

    Get-WsusUpdate -Approval Unapproved | where {$_.Update.Title -like "*Preview*"} | Deny-WsusUpdate
    start-transcript -path "C:\WSUSlogs\deny-wsus  $(get-date -f dd-MM-yyyy).txt" -Append
    Get-WsusUpdate -Approval Unapproved | where {$_.Update.Title -like "Feature update*"} | Deny-WsusUpdate
    start-transcript -path "C:\WSUSlogs\deny-wsus  $(get-date -f dd-MM-yyyy).txt" -Append
    $dotnetupdates = Get-WsusUpdate -Approval Unapproved | Where {$_.update.title -ilike "*.NET Framework*" -or $_.update.title -ilike "*.NET*"}
    $dotnetupdates | Approve-WsusUpdate -Action NotApproved -TargetGroupName "Windows10"
    $dotnetupdates | Approve-WsusUpdate -Action NotApproved -TargetGroupName "Windows Server"
    start-transcript -path "C:\WSUSlogs\deny-wsus  $(get-date -f dd-MM-yyyy).txt" -Append
    $AllUnaproved = Get-WsusUpdate -Classification All -Approval Unapproved -Status any
    $AllUnaproved | Approve-WsusUpdate -Action NotApproved -TargetGroupName "Windows Server"
    $AllUnaproved | Approve-WsusUpdate -Action NotApproved -TargetGroupName "Windows10"
    $AllUnaproved | Approve-WsusUpdate -Action NotApproved -TargetGroupName "All without .net"
    start-transcript -path "C:\WSUSlogs\deny-wsus  $(get-date -f dd-MM-yyyy).txt" -Append
     

     

    Hi here is mine but i need help about installing on the client pc

    i run this and all was ok but i was thinking will only download on client pc(client – my coworkers in my company)

    but not to install ….

    Can u help me to tell me how to change with parametars only to download but not to install…

     

    mario.kosev@gmail.com

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account