Click an Ad

If you find this blog helpful, please support me by clicking an ad!

Tuesday, June 30, 2015

Getting my Windows Clients Patched

Most of the time my WSUS group policies do the job of making my client systems update when they're supposed to, but other times it just doesn't work out. On top of that, I don't have good reporting on the patch levels of my systems. Yeah I know. It's on my list.

In the meantime, I use the script below to force computers to check for updates, and then open up a separate (awesome, not mine!) Powershell GUI app that I patch them with.

I also use this process to reboot all of my "client" machines if needed. For example, when a version of Flash comes out that fixes a vulnerability being actively exploited in the wild. Ahem. I patch Flash via Group Policy on system startup. For this, I just skip the patching functionality and use PosPAIG to reboot computers.

You can find PoshPAIG at codeplex here. Seriously, if you patch systems, even if you aren't going to use this script, check it out. I use it for my servers too. Saves me SO much time.

The process is as follows:
1. Get all the client computers from AD (with some exceptions).
2. Force them to check for updates.
3. Wait.
4. Feed the list into PoshPAIG.
5. Patch and reboot (or just reboot).

Heeeere we go - I'll talk through the scripts via the built-in documentation.

################################################################################
BEGIN SCRIPT
################################################################################
# Phase One: Getting our Data Together
################################################################################

#The path to PoshPAIG (it's a Powershell GUI that allows you to scan/patch multiple systems)
$PoshPAIGFolder = "C:\PS\PoshPAIG\Start-PoshPAIG.ps1"

#File for storing a list of which systems are initially powered on
$OutputFilePoweredOn = "c:\temp\WsusMegaCheckIn_PoweredOn.txt"

#File for storing a list of which systems weren't powered on, so I can deal with them later.
$OutputFilePoweredOff = "C:\Temp\WSUSMegaCheckIn_PoweredOff.txt"

#Remove any existing outfiles, ignoring errors
Remove-Item $OutputFilePoweredOn -force -erroraction silentlycontinue
Remove-Item $OutputFilePoweredOff -force -erroraction silentlycontinue

#Import the AD Module
import-module activedirectory

#Get the computers from AD, with some exceptions: I don't want anything from the computers container or Servers OU, nor Domain Controllers, NAS devices, or WAPs. 
$PreLimComputers = get-adcomputer -filter * -properties * | where {
$_.distinguishedname -notlike "*CN=Computers*" `
-and $_.distinguishedname -notlike "*OU=Servers*" `
-and $_.distinguishedname -notlike "*OU=Domain Controllers*" `
-and $_.Name -notlike "NAS*" `
-and $_.Name -notlike "WAP*"
}

################################################################################
# Phase Two: Refining our data a bit
################################################################################

#Now, there are a few people who get all bent out of shape if I reboot their computers (like me, and the computer I'm running this script from). Instead of adding them statically to the list above, I've created an AD group that contains their computer accounts. This allows me to use this group as an exception to any rebooting-type script that I write. So, here I will  based on NoPatchReboot Group Membership

#Create a blank array
$Groups = @("NoPatchReboot")

#Create a Regex - I'll be honest that I know what this does, but not how it does it. I just can't wrap my head around regex. BUT.....
$RegEx = '^({0})' -f ($groups -join '|')

#Now I'm going to create a new list of computers, where none of the computers are members of my NoPatchReboot AD Group, helped along by the mysterious, enigmatic regex
$Computers = $PreLimComputers | where-object {($_.MemberOf | Get-AdGroup).Name -notmatch $Regex} | select name | sort name


################################################################################
# Phase 3: Let's do some Work
################################################################################

#Foreach computer, if it is pinged successfully, send it a remote command to check for updates
#Also add it to the "Powered On" text file that you can use in PoshPAIG

Foreach ($computer in $Computers){
    $System = $Computer.Name
    If ((Test-Connection -ComputerName $System -Count 1 -Quiet) -eq $true){
        Invoke-Command -ComputerName $System -ScriptBlock {c:\windows\system32\wuauclt.exe /detectnow}
        Write-host -Foregroundcolor GREEN "$System forced to check for updates"
        $system | add-content $OutputFilePoweredOn
    } #End If
} #End Foreach

#Start PoshPAIG for me
start-process powershell -ArgumentList "-File $PoshPAIGFolder"

Write-Host "Import $OutputFilePoweredOn to patch live systems"

################################################################################
END SCRIPT
################################################################################

At this point, PoshPAIG is open, and I would import the PoweredOn text file, then scan and patch those systems.

I was going to get into my WHOLE script, but I decided that it was a little too specific in a not-applicable-to-most kind of way.

The gist is, though, that while PoshPAIG was doing it's thing, I would initialize the next phase of my script, which uses Wake-On-LAN (WOL) packets to turn machines on. Is the machine in another subnet? But, WOL packets are broadcast and can't be routed! Oh noes! Pffft. Got it covered. I know a server on site that's all to happy to send out WOL packets to machines I target.

I'll put in some code here, because it's generally useful, but this is just code snippets now.....

The first thing we need are MAC addresses for the WOL packets. I have spiceworks pull a report of IP Addresses and MAC Addresses every day and dump it out to a CSV just for this purpose. Once my Domain Controllers get onto Server 2012 I plan (hope) to replace this kludge with the new Powershell DHCP commandlets.
$MACAddressTableFromSpiceworks = Import-CSV "\\spiceworks\rpt\report-174.csv"

Once I get a MAC address, I use this to send a WOL packet (to computers on-site):
$MacByteArray = $MacAddress -split "[:-]" | ForEach-Object { [Byte] "0x$_"} 
[Byte[]] $MagicPacket = (,0xFF * 6) + ($MacByteArray  * 16)
$UdpClient = New-Object System.Net.Sockets.UdpClient
$UdpClient.Connect(([System.Net.IPAddress]::Broadcast),7)
$UdpClient.Send($MagicPacket,$MagicPacket.Length) | out-null
$UdpClient.Close() | Out-Null

What's neat about the above (which I didn't make, btw) is that it doesn't care if you use hyphens or colons as the delimiter, by using -split.

Now, if the system is at another site, based on IP address, I would send a list of systems at that site into a text file there, and then call a WOLScript to send the WOL packets to those computers, like so:

$process=[WMICLASS]"\\$SisterSiteServer\ROOT\CIMV2:win32_process"
$result=$process.Create("powershell.exe -File C:\ps\WOLScript.ps1")
$ProcessID = $result.processID

I want this script to just wait until the script at the sister site is finished running
Only when the process initiated above completes will $Running be $null, and then the while loop will end

$Running = 1
While ($Running -ne $null){
Start-Sleep -seconds 30 #wait 30 seconds
$Running = (get-process -id $ProcessID -ComputerName $SisterSiteServer -ea 0) #Check the process again
} #End While

Aaaaand now I take a break while those computers boot up ad settle down. Stop. Coffeetime.
Write-Host "Sleeping for 5 minutes to allow powered on computers to settle"
Start-Sleep -Seconds 300

And at this point I pretty much start all over, forcing those systems to check for updates
Foreach ($computer in $PoweredOffSystems){
    $System = $Computer
    If ((Test-Connection -ComputerName $System -Count 1 -Quiet) -eq $true){
        Invoke-Command -ComputerName $System -ScriptBlock {c:\windows\system32\wuauclt.exe /detectnow}
        Write-host -Foregroundcolor GREEN "$System forced to check for updates"
    } #End If
    Else {
        Write-host -Foregroundcolor RED "$System is Offline"
    } #End Else
} #End Foreach

Now I take note of any non-responsive systems so that I can investigate them later to find out why WOL didn't work.

Nowadays, that's usually because the system is on the wireless. I briefly considered removing laptops from the initial list from AD, but hey, some of them might be docked, so I'll get them while I can.

No comments:

Post a Comment