/build/static/layout/Breadcrumb_cap_w.png

How-To: Schedule Scripts for the Nth Weekday of the Month

As mentioned in the below posts, the CRON scheduler (custom schedule) on the K1000 does not support scheduling for the Nth weekday of the month (Ex. 2nd Tuesday of the Month)
    http://www.itninja.com/question/custom-schedule-for-every-2nd-friday-of-every-month
    http://www.itninja.com/blog/view/how-to-schedule-patches-on-the-3rd-saturday-of-the-month

While nothing can be done about that except for an update to the K1000, I have come up with a way to work around this in scripting (see darkhawtman's answer in the first link above for a workaround with patching).

In my organization, we need to run a cleanup script on all of our virtual machines the Sunday before patch Tuesday. Due to the CRON limitations, I wrote a Powershell function (see below) that will return specified weekday sequences.

The function can be used in multiple scenarios and has a lot of options - see the help section for details and examples.

All I had to do was add the function and an if/else statement to my script and I'm in business.

In my situation, this is what I did: 
    1. Upload a ps1 (mine is named CheckDate.ps1) to the Kscript with the below code

    2. Schedule the script to run every Sunday at 1:00 AM

    3. In the Verify section do the following: 
            Launch a program
            Directory:
            $KACE_SYS_DIR

            File:
            powershell.exe

            Parameters:
            -NoProfile -ExecutionPolicy Bypass -File $(KACE_DEPENDENCY_DIR\CheckDate.ps1))

    4. Put the cleanup script in the On Success section and a Date validation failed log message in the Remediation section.

Some explanations:

1. Why not just do the 2nd Sunday of the month?
    The second Sunday of the month is not always the Sunday before the 2nd Tuesday of the month.
        Ex. If the 2nd Tuesday of the month is the 8th, then the 2nd Sunday would be the week after that, not before (See August 2017)

2. Why is format D required for this?
    The default format of a DateTime object (Get-Date) includes the time - we only need to validate the day - if the time is in the validation, then it would return false negatives.

Function Get-WeekdaySequence {

<#
.DESCRIPTION
    This function will return the nth weekday of the input month (default is current month). For example, it can return the 2nd Tuesday of the month (Patch Tuesday).
    There are several parameters to expand this functionality listed below.
    Built from C Ashish's post on TechNet: https://social.technet.microsoft.com/Forums/ie/en-US/7b8a6966-4ea8-4b0b-8ca6-c1c8545ecc54/find-date-on-second-tuesday-of-every-month?forum=dpmpowershell

.PARAMETER WeekdaySequence
    Accepts values 1-5, determines which day of the month to return 1=1st weekday of the month, 2=2nd weekday of the month et cetera, will fail if there are not enough weekdays in the specified month
    i.e. Cannot cross months

.PARAMETER Weekday
    Accepts weekday strings as values

.PARAMETER Date
    Accepts DateTime objects (Get-Date returns a datetime object), defaults to the current date

.PARAMETER OffsetDays
    Accepts integers between -6 and 6, will return the specified weekday sequence + or - the number of days specified, can cross months (see examples)

.PARAMETER Format
    Accepts a string specifying the date format desired, defaults to 'F'. See https://technet.microsoft.com/en-us/library/ee692801.aspx?f=255&MSPPError=-2147217396 for Get-Date formats.

.EXAMPLE
Return the 2nd Tuesday of the month (Microsoft Patch Tuesday)
    Get-WeekdaySequence -WeekdaySequence 2 -Weekday Tuesday

.EXAMPLE
Return the 2nd Tuesday in the month of 12/2018
    Get-WeekdaySequence -WeekdaySequence 2 -Weekday Tuesday -Date (Get-Date 12/2018)

.EXAMPLE
Return the Sunday before the 2nd Tuesday of the month
    Get-WeekdaySequence -WeekdaySequence 2 -Weekday Tuesday -OffsetDays -2

.EXAMPLE
Return the Thursday after the 3rd Tuesday of the month
    Get-WeekdaySequence -WeekdaySequence 2 -Weekday Tuesday -OffsetDays 2

.Example
Return the Sunday before the 2nd Thursday of 11/2030 in the LongDatePattern format
    Get-WeekdaySequence -WeekdaySequence 2 -Weekday Thursday -Date 11/2030 -OffsetDays -4 -Format D
#>

    Param (
        
        [Parameter(Mandatory=$true,HelpMessage="1 for 1st Weekday of the month, 2 for 2nd weekday of the month, etc.")]
        [ValidateSet(1,2,3,4,5)]
        [ValidateNotNullOrEmpty()]
        $WeekdaySequence,

        [parameter(Mandatory=$true)]
        [ValidateSet("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday")]
        [ValidateNotNullOrEmpty()]
        [String]$Weekday,

        [datetime]$Date=(Get-Date),

        [ValidateRange(-6,6)]
        [int]$OffsetDays,

        [Parameter(HelpMessage="Specify the Get-Date format desired")]
        [ValidateNotNullOrEmpty()]
        [String]$Format='F'

    )

    [datetime]$MonthBegin=$Date.Month.ToString()+'/1/'+$Date.Year.ToString()

    while ($MonthBegin.DayofWeek -ine $WeekDay) {
    
        $MonthBegin = $MonthBegin.AddDays(1)

    }

    $ReturnDate = $MonthBegin.AddDays(7*($WeekdaySequence-1))

    if ($ReturnDate.Month -ne $Date.Month) {

        return (Write-Error -Exception "Invalid Sequence Number" -Message "There are not $WeekdaySequence $($Weekday)s in $($Date.Month)\$($Date.Year)")

    }

    if (!$OffsetDays) {
    
        Return (Get-Date $ReturnDate -Format $Format)

    }

    else {
        
        Return (Get-Date ($ReturnDate.AddDays($OffsetDays)) -Format $Format)

    }

}

if ((Get-WeekdaySequence -WeekdaySequence 2 -Weekday Tuesday -OffsetDays -2 -Format D) -eq (Get-Date -Format D)) {

    Exit 0

}

else {

    Exit 98

}

Comments

This post is locked
 
This website uses cookies. By continuing to use this site and/or clicking the "Accept" button you are providing consent Quest Software and its affiliates do NOT sell the Personal Data you provide to us either when you register on our websites or when you do business with us. For more information about our Privacy Policy and our data protection efforts, please visit GDPR-HQ