I’ve been a bit remiss with my promise to provide technical explanations to the practices detailed in Patch Like You Mean It. As many of you already know, the constant diversions that come from parenthood, marriage, and life in general compete for one’s attention in ever more interesting ways.
That said, you have my attention now. While this function is relatively simple and there are a variety of good alternatives available elsewhere (Including the original inspiration by BarryCWT), I do want to get into the habit of explaining the constituent pieces of how the patching process comes together and why I may sometimes create a derivative work of something that was already great prior to my having done anything at all.
function Get-PatchTuesday { [CmdletBinding()] param ( [Parameter(Position = 1, HelpMessage = 'Please enter a valid month as a number (1 for Jan, 12 for Dec).')] [ValidateSet(1,2,3,4,5,6,7,8,9,10,11,12)] [string]$Month=$(Get-Date).Month, [Parameter(Position = 2, HelpMessage = 'Please select a valid year in the #### format.')] [ValidateSet('2018','2019','2020','2021','2022','2023','2024','2025')] [string]$Year=$(Get-Date).Year ) [datetime]$firstDayOfTheMonth = (-join ($Month,'/1/',$Year)) [System.Object]$monthLength = 0..31 [System.Collections.ArrayList]$tuesdayDates = @() foreach ($day in $monthLength) { [datetime]$loopDay = $firstDayOfTheMonth.AddDays($day) if ($loopDay.DayOfWeek -eq 'Tuesday') { $tuesdayDates.Add($loopDay) | Out-Null } } $tuesdayDates | Select-Object -Index 1 }
To PowerShell aficionados, this is a relatively straightforward function…but I remember a time where this would have looked like gibberish to me so I’m going to break it down into smaller chunks.
param ( [Parameter(Position = 1, HelpMessage = 'Please enter a valid month as a number (1 for Jan, 12 for Dec).')] [ValidateSet(1,2,3,4,5,6,7,8,9,10,11,12)] [string]$Month=$(Get-Date).Month, [Parameter(Position = 2, HelpMessage = 'Please select a valid year in the #### format.')] [ValidateSet('2018','2019','2020','2021','2022','2023','2024','2025')] [string]$Year=$(Get-Date).Year )
This section is where our parameter settings are established. Lines 3 and 8 dictate the parameter position for shorthand usage (In the event that you want to use the function in the following way: Get-PatchTuesday 11 2019, where ’11’ occupies the first position and ‘2019’ occupies the second). Lines 5 and 10 are the ValidateSet sections and constrain the inputs that can be passed to the function, reducing the potential for confusion. Lines 6 and 11 set the default value and, in this instance, establish that invoking Get-PatchTuesday without any additional input will indicate to the function that you want the Patch Tuesday for the current month and year.
[datetime]$firstDayOfTheMonth = (-join ($Month,'/1/',$Year)) [System.Object]$monthLength = 0..31 [System.Collections.ArrayList]$tuesdayDates = @()
The variable declarations are a little more straightforward. Some aspects of what you’re seeing here are pretty much consistent in everything I’ll post:
- I leverage camel-case variable and parameter structure (E.g., $thisVariable.thisParameter).
- More and more, I explicitly declare the data type that I intend for a variable to take. PowerShell does a fairly good job of interpreting your input but I’ve found that it’s simply far less frustrating to make your declarations up front rather than dealing with interpreter oddities while debugging later. For more information on this topic, 4sysops has a really good post that explains variables, data types, and examples of some of those oddities that can pop up if you don’t explicitly define things.
$firstDayOfTheMonth is simply constructing a datetime value to give our upcoming code a starting point. $monthLength is there to hold an array of the maximum number of days that a month can have. $tuesdayDates is an empty array that will be populated by the Tuesday date values for the month that was selected for the execution.
foreach ($day in $monthLength) { [datetime]$loopDay = $firstDayOfTheMonth.AddDays($day) if ($loopDay.DayOfWeek -eq 'Tuesday') { $tuesdayDates.Add($loopDay) | Out-Null } } $tuesdayDates | Select-Object -Index 1 }
The foreach loop that rounds out our efforts here is looping through every day in the $monthLength array, establishing a temporary $loopDay variable to hold the value of the $firstDayOfTheMonth.AddDays($day) value, evaluating whether or not the $loopDay is a Tuesday, and adding the value to the $tuesdayDates variable if it is indeed a Tuesday.
The last operation of the script is to return the 2nd Tuesday of the month. In PowerShell and many(Most?) other languages, an array’s index starts with 0 which is why “-Index 1” represents the 2nd Tuesday value in the array.