Schedule mismatches

We see some problem around the scheduling/handleing of time.

We are working on a script, that would use a schedule / Cron to deprovision users.

The entire script of the API is quite lengthy, but would look like this:


    param(
        [Parameter(Mandatory = $true)]
        [string]$RequestId,
    
        # [Parameter(Mandatory = $false)]
        # [string]$ForwardEmailAddress,
    
        # [Parameter(Mandatory = $false)]
        # [string]$DirectReportList,
    
        [Parameter(Mandatory = $false)]
        [string]$ServiceDeskBaseUrl = "https://ictportal/api/v3/requests/",
    
        [Parameter(Mandatory = $false)]
        [string]$AuthToken = "<token removed>", #TODO: move this to PSU-s "secrets"
    
        [Parameter(Mandatory = $false)]
        [string]$ADCredentialName = "ADCredentialPRD",
    
        [Parameter(Mandatory = $false)]
        [string]$FunctionFolder = "D:\FS_Workspace\powershell\MESDP-API_interactions\leaver-functions", #TODO: is this really needed?
    
        [Parameter(Mandatory = $false)]
        [string]$ComputerName = ($env:COMPUTERNAME),
    
        [Parameter(Mandatory = $false)]
        [string]$ControllerScript = "controller-leaver.ps1", #TODO: ensure this is in PSU! 
    
        [Parameter(Mandatory = $false)]
        [string]$UpdateTicketScript = "D:\FS_Workspace\powershell\MESDP-API_interactions\Update-Ticket.ps1" #TODO: this script might need to be relocated --> path is temporary
    )

    begin {
        # Initialize result object to track operations throughout the script
        $resultObject = [PSCustomObject]@{
            Status         = "In Progress"
            Steps          = @()
            Errors         = @()
            UserInfo       = $null
            ExpiryDate     = $null
            ScheduleName   = $null
            CronExpression = $null
            UserNotFound   = $false
            AttemptedUser  = $null
        }

        function Add-ProcessStep {
            param(
                [string]$StepName,
                [string]$Status,
                [string]$Message
            )
        
            $resultObject.Steps += [PSCustomObject]@{
                Step      = $StepName
                Status    = $Status
                Message   = $Message
                Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            }
        }

        function Add-Error {
            param(
                [string]$StepName,
                [string]$ErrorMessage
            )
        
            $resultObject.Errors += [PSCustomObject]@{
                Step      = $StepName
                Message   = $ErrorMessage
                Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            }
        
            Add-ProcessStep -StepName $StepName -Status "Failed" -Message $ErrorMessage
        }

        function Convert-UnixTimeToCron {
            param(
                [Parameter(Mandatory = $true)]
                [int64]$InputUnixTime
            )
    
            try {
                # Get the current time
                $currentTime = Get-Date
        
                # Start from epoch (1970-01-01 00:00:00)
                $epochStart = Get-Date "1970-01-01 00:00:00"
        
                # Convert Unix time (milliseconds) to DateTime
                $dateTime = $epochStart.AddSeconds($InputUnixTime / 1000)
       
        
                # Check if the time is in the past
                if ($dateTime -lt $currentTime) {
                    # If in the past, use current date and add a few minutes
                    $dateTime = $currentTime.AddMinutes(5) # Add 5 minutes (adjust as needed)
                    Add-ProcessStep -StepName "TimeConversion" -Status "Warning" -Message "Specified time was in the past. Adjusted to current time + 5 minutes."
                }         
        
                # Extract minute, hour, day, month, and weekday from the adjusted dateTime
                $minute = $dateTime.Minute
                $hour = $dateTime.Hour
                $day = $dateTime.Day
                $month = $dateTime.Month
                $weekday = $dateTime.DayOfWeek
        
                # Map weekday to cron format (0 = Sunday, 6 = Saturday)
                $weekdayArray = @(0, 1, 2, 3, 4, 5, 6)
                $cronWeekday = $weekdayArray[$weekday]
        
                # Return the cron expression
                Write-Host "SETTING SCHEDULE: '$minute $hour $day $month $cronWeekday'"
                return "$minute $hour $day $month $cronWeekday"
            }
            catch {
                Add-Error -StepName "TimeConversion" -ErrorMessage "Failed to convert Unix time to Cron: $_"
                throw
            }
        }

        function Convert-UnixTimeToUtcDateTime {
            param (
                [Parameter(Mandatory)]
                [int64]$InputUnixTime
            )

            $utcTime = [System.DateTimeOffset]::FromUnixTimeMilliseconds($InputUnixTime).UtcDateTime
            $timezone = [System.TimeZoneInfo]::FindSystemTimeZoneById("GMT Standard Time")

            if ($timezone.IsDaylightSavingTime($utcTime)) {
                return [System.TimeZoneInfo]::ConvertTimeFromUtc($utcTime, $timezone).ToString("yyyy-MM-dd HH:mm:ss")
            }

            return $utcTime.ToString("yyyy-MM-dd HH:mm:ss")
        }
    
        function Get-SAMAccountName {
            param (
                [Parameter(Mandatory = $true)]
                [string]$firstName,
            
                [Parameter(Mandatory = $true)]
                [string]$surname,
            
                [Parameter(Mandatory = $true)]
                [string]$requestId
            )
    
            try {
                # Ensure that the Active Directory module is loaded
                if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
                    $errorMsg = "Active Directory module not found."
                    Add-Error -StepName "ADModuleCheck" -ErrorMessage $errorMsg
                    throw $errorMsg
                }
        
                Import-Module ActiveDirectory
        
                # Search for the user(s) in Active Directory
                $users = Get-ADUser -Filter { GivenName -eq $firstName -and Surname -eq $surname } -Properties SAMAccountName
        
                # Check if no users were found
                if ($users.Count -eq 0) {
                    $errorMsg = "No users found with the given first name ($firstName) and last name ($surname)."
                    Add-Error -StepName "ADUserLookup" -ErrorMessage $errorMsg
                
                    # Set user not found information in the result object
                    $resultObject.UserNotFound = $true
                    $resultObject.AttemptedUser = [PSCustomObject]@{
                        FirstName = $firstName
                        Surname   = $surname
                        RequestId = $requestId
                    }
                
                    throw $errorMsg
                }
            
                Add-ProcessStep -StepName "ADUserLookup" -Status "Success" -Message "Found user $firstName $surname with SAMAccountName: $($users[0].SAMAccountName)"
                # Output the SAMAccountName
                return $users | Select-Object -ExpandProperty SAMAccountName
            }
            catch {
                if ($_.Exception.Message -ne "No users found with the given first name ($firstName) and last name ($surname).") {
                    Add-Error -StepName "ADUserLookup" -ErrorMessage "Error finding user: $_"
                }
                throw
            }
        }
    
        function Get-RequestData {
            param(
                [Parameter(Mandatory = $true)]
                [string]$RequestId,
            
                [Parameter(Mandatory = $true)]
                [string]$AuthToken,
            
                [Parameter(Mandatory = $true)]
                [string]$ServiceDeskBaseUrl
            )
    
            try {
                $url = "$ServiceDeskBaseUrl$RequestID"
                $technician_key = @{
                    "authtoken" = $AuthToken
                    "accept"    = 'application/v3.0+json'
                }
            
                $response = Invoke-RestMethod -Uri $url -Method Get -Headers $technician_Key -ErrorAction Stop
            
                $requestData = @{
                    "firstName"           = $response.request.udf_fields.udf_sline_926
                    "surname"             = $response.request.udf_fields.udf_sline_925
                    "date"                = $response.request.udf_fields.udf_date_5116.value
                    "business"            = $response.request.udf_fields.udf_pick_4294
                    "directReports"       = $response.request.udf_fields.udf_pick_16971.name
                    "directReportList"    = $response.request.udf_fields.udf_sline_16972
                    "newManager"          = $response.request.udf_fields.udf_sline_16973
                    "mailboxConversion"   = $response.request.udf_fields.udf_pick_16905.name
                    "forwardEmailAddress" = $response.request.udf_fields.udf_sline_16906
                }
            
                Add-ProcessStep -StepName "GetRequestData" -Status "Success" -Message "Retrieved request data for $($requestData.firstName) $($requestData.surname)"
                return $requestData
            }
            catch {
                Add-Error -StepName "GetRequestData" -ErrorMessage "Failed to retrieve request data: $_"
                throw
            }
        }

        function Convert-UnixTimeToDateTime {
            param (
                [Parameter(Mandatory = $true)]
                [int64]$InputUnixTime
            )
        
            try {
                $dateTime = (Get-Date "1970-01-01 00:00:00Z").AddSeconds($InputUnixTime / 1000)
                $currentTime = Get-Date
                # Check if the time is in the past
                if ($dateTime -lt $currentTime) {
                    # If in the past, use current date and add a few minutes
                    $dateTime = $currentTime.AddMinutes(5) # Add 5 minutes (adjust as needed)
                }

                Add-ProcessStep -StepName "DateTimeConversion" -Status "Success" -Message "Converted Unix time to DateTime: $dateTime"
                return $dateTime
            }
            catch {
                Add-Error -StepName "DateTimeConversion" -ErrorMessage "Failed to convert Unix time to DateTime: $_"
                throw
            }
        }

        function Set-Expiry {
            param(
                [Parameter(Mandatory = $true)]
                [int64]$Date,
            
                [Parameter(Mandatory = $true)]
                [string]$RequestId,
            
                [Parameter(Mandatory = $true)]
                [string]$FirstName,
            
                [Parameter(Mandatory = $true)]
                [string]$Surname,
            
                [Parameter(Mandatory = $true)]
                [string]$BusinessName,
            
                [Parameter(Mandatory = $true)]
                [string]$ADCredentialName
            )
        
            try {
                # Ensure the Active Directory module is imported
                Import-Module ActiveDirectory -ErrorAction Stop
            
                $ADCredential = $secret:ADCredentialPRD #Get-Variable -Name $ADCredentialName -ValueOnly -Scope Script
            
                # Function to find the SAMAccountName using FirstName and Surname
                function Find-SAMAccountName {
                    param (
                        [Parameter(Mandatory = $true)]
                        [string]$FirstName,
                    
                        [Parameter(Mandatory = $true)]
                        [string]$Surname,
                    
                        [Parameter(Mandatory = $true)]
                        [PSCredential]$Credential
                    )
                
                    # Search for the user in Active Directory
                    $Filter = "GivenName -eq '$FirstName' -and Surname -eq '$Surname'"
                    $User = Get-ADUser -Filter $Filter -Properties SAMAccountName -Credential $Credential -ErrorAction Stop
                
                    if ($User) {
                        return $User.SAMAccountName
                    }
                    else {
                        $errorMsg = "Error: User not found with the name '$FirstName $Surname'."
                        Add-Error -StepName "ADUserLookup" -ErrorMessage $errorMsg
                        throw $errorMsg
                    }
                }
            
                # Function to set account expiration date using SAMAccountName
                function Set-AccountExpiration {
                    param (
                        [Parameter(Mandatory = $true)]
                        [string]$SAMAccountName,
                    
                        [Parameter(Mandatory = $true)]
                        [datetime]$ExpiryDate,
                    
                        [Parameter(Mandatory = $true)]
                        [PSCredential]$Credential
                    )
                
                    # Set the account expiration date
                    Set-ADUser -Identity $SAMAccountName -AccountExpirationDate $ExpiryDate -Credential $Credential -ErrorAction Stop
                    Add-ProcessStep -StepName "SetExpiry" -Status "Success" -Message "Account expiration date for $SAMAccountName set to $ExpiryDate"
                    return $true
                }
            
                # Convert Unix time to DateTime
                $ExpiryDate = Convert-UnixTimeToDateTime -InputUnixTime $Date
                $resultObject.ExpiryDate = $ExpiryDate
            
                # Find the SAMAccountName in Active Directory
                $SAMAccountName = Find-SAMAccountName -FirstName $FirstName -Surname $Surname -Credential $ADCredential
            
                # If SAMAccountName is found, set the account expiration date
                if ($SAMAccountName) {
                    $success = Set-AccountExpiration -SAMAccountName $SAMAccountName -ExpiryDate $ExpiryDate -Credential $ADCredential
                    if ($success) {
                        return 0 # Success exit code
                    }
                }
            
                return 1 # Error exit code
            }
            catch {
                Add-Error -StepName "SetExpiry" -ErrorMessage "Failed to set account expiry date: $_"
                return 1 # Error exit code
            }
        }
    }

    process {
        try {
            # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
            #                                                                                       0. Store the request data                                                                                      #
            # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
            try {
                $RequestData = Get-RequestData -RequestID $RequestID -AuthToken $AuthToken -ServiceDeskBaseUrl $ServiceDeskBaseUrl
                $resultObject.UserInfo = [PSCustomObject]@{
                    FirstName = $RequestData.firstName
                    Surname   = $RequestData.surname
                    LeaveDate = $RequestData.date
                    Business  = $RequestData.business
                }
            }
            catch {
                $errorMsg = "Failed to retrieve request data: $_"
                Add-Error -StepName "RequestDataRetrieval" -ErrorMessage $errorMsg
                throw $errorMsg
            }

            # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
            #                                                                                      1. Ensure user found in AD                                                                                      #
            # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
            try {
                $SAMAccountName = Get-SAMAccountName -FirstName $($RequestData.firstName) -Surname $($RequestData.surname) -RequestId $RequestId
                if (-not $SAMAccountName) {
                    $errorMsg = "Unable to determine SAMAccountName for user $($RequestData.firstName) $($RequestData.surname)"
                    Add-Error -StepName "SAMAccountNameLookup" -ErrorMessage $errorMsg
                    throw $errorMsg
                }
                Add-ProcessStep -StepName "SAMAccountNameLookup" -Status "Success" -Message "Found SAMAccountName: $SAMAccountName"
            } 
            catch {
                if (-not ($resultObject.Errors | Where-Object { $_.Step -eq "ADUserLookup" })) {
                    Add-Error -StepName "SAMAccountNameLookup" -ErrorMessage "Failed to get SAMAccountName: $_"
                }
                throw
            }

            # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
            #                                                                                    2. Set expiry of the user found                                                                                   #
            # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
            $SEParams = @{
                FirstName        = $($RequestData.firstName)
                Surname          = $($RequestData.surname)
                Date             = $($RequestData.date)
                BusinessName     = $($RequestData.business)
                RequestId        = $RequestId
                ADCredentialName = $ADCredentialName
            }

            Add-ProcessStep -StepName "SetExpiryStart" -Status "InProgress" -Message "Setting expiry date for user: $($RequestData.firstName) $($RequestData.surname)"
            $exitCode = Set-Expiry @SEParams

            if ($exitCode -eq 0) {
                Add-ProcessStep -StepName "SetExpiryComplete" -Status "Success" -Message "Successfully set expiry date for user"

                # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
                #                                                                                     3. Schedule leaver processing                                                                                    #
                # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
                # Prepare unique schedule name
                $ScheduleName = "$RequestId-$($RequestData.firstName)-$($RequestData.surname)"
                $resultObject.ScheduleName = $ScheduleName

                # Convert UnixTime to Cron format for scheduling
                $CronExpression = Convert-UnixTimeToCron $Date
                #$OneTime = (convert-UnixTimeToUtcDateTime).ToUniversalTime()
                $resultObject.CronExpression = $cronExpression
            
                $OneTime = Convert-UnixTimeToDateTime -InputUnixTime $Date

                # Define parameters for follow-up script
                $FollowUpParams = @{
                    RequestId      = $RequestId
                    SAMAccountName = $SAMAccountName
                }

                Add-ProcessStep -StepName "SchedulingStart" -Status "InProgress" -Message "Scheduling follow-up script for $OneTime"
            
                try {
                    # This line is kept as per requirements
                    New-PSUSchedule -Name $ScheduleName -Script $ControllerScript -Parameters $FollowUpParams -Computer $ComputerName -Cron $CronExpression -TimeZone "GMT Standard Time" #-OneTime $OneTime
                
                    Add-ProcessStep -StepName "SchedulingComplete" -Status "Success" -Message "Successfully scheduled leaver processing for $OneTime"
                    $resultObject.Status = "Success"
                }
                catch {
                    Add-Error -StepName "Scheduling" -ErrorMessage "Failed to schedule follow-up script: $_"
                    $resultObject.Status = "Partial - Expiry Set but Scheduling Failed"
                    throw
                }
            }
            else {
                Add-Error -StepName "SetExpiryFailed" -ErrorMessage "Failed to set account expiry date. Follow-up script will not be scheduled."
                $resultObject.Status = "Failed - Could Not Set Expiry Date"
                throw "Failed to set account expiry date"
            }
        }
        catch {
            if (-not ($resultObject.Errors.Count -gt 0)) {
                Add-Error -StepName "UnhandledException" -ErrorMessage "Unhandled exception occurred: $_"
            }
            $resultObject.Status = "Failed"
        }
    }


    end {
        # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
        #                                                                                    4. End block - register outcome                                                                                   #
        # ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #
        # Create worklog description and update original ticket
        $worklogSummary = "Leaver Processing Summary:`n"
        $worklogSummary += "Status: $($resultObject.Status)`n"
    
        if ($resultObject.UserInfo) {
            $worklogSummary += "User: $($resultObject.UserInfo.FirstName) $($resultObject.UserInfo.Surname)`n"
        }
    
        # Add specific note about user not found if applicable
        if ($resultObject.UserNotFound) {
            $userNotFoundNote = "ATTENTION: The user $($resultObject.AttemptedUser.FirstName) $($resultObject.AttemptedUser.Surname) was not found in Active Directory. Please check that the First Name and Surname match the request. If not, please notify the requestor and make the necessary changes."
            $worklogSummary = "$userNotFoundNote`n`n$worklogSummary"
            $worklogSummary += "ACTION REQUIRED: Verify user details in Active Directory`n"
        }
    
        if ($resultObject.ExpiryDate) {
            $worklogSummary += "Expiry Date Set: $($resultObject.ExpiryDate)`n"
        }
    
        if ($resultObject.ScheduleName) {
            $worklogSummary += "Processing Scheduled: Yes`n"
            $worklogSummary += "Schedule Name: $($resultObject.ScheduleName)`n"
        }
    
        if ($resultObject.Errors.Count -gt 0) {
            $worklogSummary += "`nErrors Encountered:`n"
            foreach ($error in $resultObject.Errors) {
                $worklogSummary += "- [$($error.Step)] $($error.Message)`n"
            }
        }
    
        $worklogSummary += "`nProcessing Steps:`n"
        foreach ($step in $resultObject.Steps) {
            $worklogSummary += "- [$($step.Status)] $($step.Step): $($step.Message)`n"
        }
    
        try {
            # Update the ticket with the results
            $updateParams = @{
                TicketID           = $RequestId
                WorklogDescription = $worklogSummary
                ResolutionContent  = $worklogSummary
                TicketType         = "UserProcessing"
            }
        
            # If process was successful and user was found, close the ticket
            # if ($resultObject.Status -match "Success" -and -not $resultObject.UserNotFound) {
            #     $updateParams.CloseTicket = $true
            # }
    
            # Call Update-Ticket to update the ticket
            & $UpdateTicketScript @updateParams

            # Write out worklog on console
            return $worklogSummary
            # return $updateParams
        }
        catch {
            Write-Error "Failed to update ticket with results: $_"
        }
    }

The output of a run looks like this:

Monday, June 9, 2025 1:44:47 PM
{
  "RequestId": "112323"
}
Requested HTTP/1.1 POST with 29-byte payload
Received HTTP/1.1 response of content type application/json of unknown size
Content encoding: utf-8
Id                  : 3
Identity            : 
AppToken            : 
Cron                : 49 13 9 6 1
NextExecution       : 
Description         : 
Script              : apis\controller-leaver.ps1
TimeZoneString      : GMT Standard Time
Continuous          : False
Delay               : 
Credential          : 
OneTime             : 
Environment         : 
Parameters          : {@{Id=5; Name=SAMAccountName; Value=<Objs 
                      Version="1.1.0.1" 
                      xmlns="http://schemas.microsoft.com/powershell/2004/04">
                        <S>Dhruvkumar.Vadkar</S>
                      </Objs>; Type=System.String; Variable=False; 
                      DisplayValue=Dhruvkumar.Vadkar; 
                      ObjectValue="Dhruvkumar.Vadkar"; BoolValue=False; 
                      StringValue=Dhruvkumar.Vadkar; StringArrayValue=; 
                      IntegerValue=0; DateValue=1/1/0001 12:00:00 AM; 
                      SecureStringValue=; ValidValues=}, @{Id=6; 
                      Name=RequestId; Value=<Objs Version="1.1.0.1" 
                      xmlns="http://schemas.microsoft.com/powershell/2004/04">
                        <S>112323</S>
                      </Objs>; Type=System.String; Variable=False; 
                      DisplayValue=112323; ObjectValue="112323"; 
                      BoolValue=False; StringValue=112323; StringArrayValue=; 
                      IntegerValue=0; DateValue=1/1/0001 12:00:00 AM; 
                      SecureStringValue=; ValidValues=}}
Name                : 112323-Dhruvkumar-Vadkar
Paused              : False
Timeout             : 0
Condition           : 
RandomDelay         : False
Computer            : INFR-VL-AUT-02
ReadOnly            : False
OneWayOneTime       : False
RandomDelayMaximum  : 60
AvailableInBranch   : {}
AvailableInBranches : {}
Module              : 
EveryHour           : False
Minute              : 0
EveryDay            : False
Hour                : 0
DayOfWeek           : False
Day                 : 
EveryMonth          : False
DayOfMonth          : 0
SpecificMonth       : False
Month               : 
ScheduleString      : 
Valid               : True
Leaver Processing Summary:
Status: Success
User: Dhruvkumar Vadkar
Expiry Date Set: 06/09/2025 13:49:48
Processing Scheduled: Yes
Schedule Name: 112323-Dhruvkumar-Vadkar

Processing Steps:
- [Success] GetRequestData: Retrieved request data for Dhruvkumar Vadkar
- [Success] ADUserLookup: Found user Dhruvkumar Vadkar with SAMAccountName: 
- [Success] SAMAccountNameLookup: Found SAMAccountName: Dhruvkumar.Vadkar
- [InProgress] SetExpiryStart: Setting expiry date for user: Dhruvkumar Vadkar
- [Success] DateTimeConversion: Converted Unix time to DateTime: 06/09/2025 13:49:48
- [Success] SetExpiry: Account expiration date for Dhruvkumar.Vadkar set to 06/09/2025 13:49:48
- [Success] SetExpiryComplete: Successfully set expiry date for user
- [Warning] TimeConversion: Specified time was in the past. Adjusted to current time + 5 minutes.
- [Success] DateTimeConversion: Converted Unix time to DateTime: 06/09/2025 13:49:48
- [InProgress] SchedulingStart: Scheduling follow-up script for 06/09/2025 13:49:48
- [Success] SchedulingComplete: Successfully scheduled leaver processing for 06/09/2025 13:49:48

Key points to note:

  • currently we are in GMT+1 (British Summer time) and the time is 1:46.
# get-date

09 June 2025 13:45:36


#  get-timezone

Id                         : GMT Standard Time
HasIanaId                  : False
DisplayName                : (UTC+00:00) Dublin, Edinburgh, Lisbon, London
StandardName               : GMT Standard Time
DaylightName               : GMT Daylight Time
BaseUtcOffset              : 00:00:00
SupportsDaylightSavingTime : True
  • as the ticket was for a past-leaver, logic was, schedule should have been current time + 5 min; script ran at 13:44 → so this should have been 13:49

  • the CRON matches this:
    Cron : 49 13 9 6 1

  • (this also matches the output we see)

  • the subsequent PSU schedule’s text is correct, but the time is not:

  • the schedules.ps1 is correct, but the scheduled job not ran at the given time:

 cat C:\ProgramData\UniversalAutomation\Repository\.universal\schedules.ps1
New-PSUSchedule -Cron "49 13 9 6 1" -Script "apis\controller-leaver.ps1" -TimeZone "GMT Standard Time" -Parameters @{
    RequestId      = '112323'
    SAMAccountName = 'Dhruvkumar.Vadkar'
} -Name "112323-Dhruvkumar-Vadkar" -Computer "INFR-VL-AUT-02"

(We observed this on the previous test last Friday too; at that time the schedule was set to 3:xx, the schedule was saying 3:xx but actually the job run at 4:xx)

What are we missing? It is a bit confusing…would assume PSU uses UTC, but the CRON/schedule is at odds with the GUI…

Thanks,
Fabrice

Following up on this, the “Jobs” shows this ran an hour late:

image