Thursday, October 2, 2014

Calculating “Patch Tuesday” with PowerShell

We write many scripts related to Microsoft patching.  A script to check for patches.  A script to patch pilot servers the day patches come out.  A script to patch production a week later.  A script to restart servers the weekend after patching.  A script to put SCOM monitors in maintenance mode during patch windows.

Many of these scripts need to know when Now is, as it relates to Patch Tuesday.  So how do we tell the scripts when Patch Tuesday is?

There are a few ways to do it, but my favorite is to calculate from the 12th of the month.  The 12th is the only day of the month that is always in the same calendar week as Patch Tuesday, so we can start there as a base.

Get-Date with no parameters gives you today, right now.  If you give it some parameters, but not enough for a full date, it uses today’s date and time to fill in the missing information.  The upshot of this is if you give Get-Date just a day, such as the 12th, it will give us the 12th of this month.

Get-Date -Day 12

It also gives us the current time.  I don’t want the calculated time to vary depending on when I run the script.  So I take the result, and tell it to give me just the date.  The result is still a datetime object, so it is set to midnight, at the beginning of the 12th of the month.

$BaseDate = ( Get-Date -Day 12 ).Date

So how do we get from the 12th to Tuesday, which might be earlier or later in the week?  We ask the 12th what day of the week it is.

$BaseDate.DayOfWeek

gives us Friday.  (Well, it did the month I wrote this.)  That “Friday” value is not really a string.  It is an enumeration.  That means that behind the word is an integer.  0 for Sunday through 7 for Saturday.

[int]$BaseDate.DayOfWeek

gives us 5.  That means we can do math with it. If we subtract the day of the week integer from the base date, we get the Sunday of the week of Patch Tuesday.  If we add two days to that Sunday, we get Patch Tuesday.  If we do the adding and the subtracting at the same time, it looks like this:

$PatchTuesday = $BaseDate.AddDays( 2 - [int]$BaseDate.DayOfWeek )

And there it is.  Patch Tuesday of this month.

But that might not be good enough.  If it's early in the month, we may need to know where we are relative to last month's Patch Tuesday.  For example, if you patch your Production servers on the third Sunday after patch Tuesday, and then run a script on Monday to confirm everything is patched, that Monday will sometimes fall in the following month.

So let's check to see if today is before this month's Patch Tuesday.

If ( (Get-Date) -lt $PatchTuesday )
{

If so, we want last month's Patch Tuesday instead.  Move the base date back one month, and recalculate

$BaseDate = $BaseDate.AddMonths( -1 )
$PatchTuesday = $BaseDate.AddDays( 2 - [int]$BaseDate.DayOfWeek )
}

Done and done.  Here is the full snippet to calculate the most recent past Patch Tuesday.

$BaseDate = ( Get-Date -Day 12 ).Date
$PatchTuesday = $BaseDate.AddDays( 2 - [int]$BaseDate.DayOfWeek )
If ( (Get-Date) -lt $PatchTuesday )
{
$BaseDate = $BaseDate.AddMonths( -1 )
$PatchTuesday = $BaseDate.AddDays( 2 - [int]$BaseDate.DayOfWeek )
}


Planning ahead?  We can calculate the next Patch Tuesday by flipping the -lt to a -gt and the -1 to a 1.

$BaseDate = ( Get-Date -Day 12 ).Date
$PatchTuesday = $BaseDate.AddDays( 2 - [int]$BaseDate.DayOfWeek )
If ( (Get-Date) -gt $PatchTuesday )
{
$BaseDate = $BaseDate.AddMonths( 1 )
$PatchTuesday = $BaseDate.AddDays( 2 - [int]$BaseDate.DayOfWeek )
}

Some might argue that this is the more important version, because it can also be used for calculating the next meeting of the Twin Cities PowerShell User Group.

Thanks to Bill Carlson for posing the challenge.