Public/New-gMSAScheduledTask.ps1

Function New-gMSAScheduledTask {

    <#
        .SYNOPSIS
            Creates a scheduled task using a Group Managed Service Account (gMSA).

        .DESCRIPTION
            This advanced function allows you to create a scheduled task that uses a gMSA to
            run specified actions. You can set the task to run either on specific days of
            the week (Weekly) or several times per day (Daily).

        .PARAMETER TaskName
            The name of the scheduled task.

        .PARAMETER TaskAction
            The arguments to pass to the executable (e.g., the script or action to run).

        .PARAMETER ActionPath
            The full path to the executable or script that will be run by the task.

        .PARAMETER gMSAAccount
            The Group Managed Service Account (gMSA) that will run the task.

        .PARAMETER Description
            A description for the scheduled task.

        .PARAMETER TriggerType
            The type of trigger to use (either "Daily" or "Weekly").

        .PARAMETER StartTime
            The time the task will start (HH:mm format).

        .PARAMETER DaysOfWeek
            Days of the week on which the task will run (for Weekly triggers).

        .PARAMETER TimesPerDay
            The number of times the task should run per day (for Daily triggers).
            Accepts values: 1, 2, 3, 4, 6, 8, 12, 24 or 48.

        .EXAMPLE
            New-gMSAScheduledTask -TaskName "MyDailyTask" -TaskAction "-ExecutionPolicy ByPass -NoLogo -File 'C:\Scripts\ MyScript.ps1'"
            -ActionPath "pwsh.exe" -gMSAAccount "gmsaTaskAccount$"
            -TriggerType "Daily" -StartTime "09:00" -TimesPerDay 4

            Creates a scheduled task that runs four times per day at intervals starting
            at 9:00 AM using the specified gMSA.

        .EXAMPLE
            New-gMSAScheduledTask -TaskName "MyWeeklyTask" -TaskAction "-ExecutionPolicy ByPass -NoLogo -File 'C:\Scripts\ MyScript.ps1'" -ActionPath "pwsh.exe" -gMSAAccount $x -TriggerType "Weekly" -StartTime "08:00" -DaysOfWeek Monday,Wednesday,Friday

            Creates a scheduled task that runs every Monday, Wednesday, and Friday
            at 8:00 AM using the specified gMSA.

        .INPUTS
            None. You must provide all inputs.

        .OUTPUTS
            Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/TaskScheduler/MSFT_ScheduledTask
            The function outputs a Scheduled Task object upon successful creation.

        .NOTES
            This script requires Windows Task Scheduler cmdlets and an Active Directory
            environment where gMSA is enabled.
    #>


    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([Microsoft.Management.Infrastructure.CimInstance])]
    # [Microsoft.Management.Infrastructure.CimInstance#ROOT / Microsoft/Windows/TaskScheduler/MSFT_ScheduledTask]

    param (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'Specify the name of the Scheduled Task.',
            Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]
        $TaskName,

        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'Specify the action that the task will run (arguments for the executable).',
            Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]
        $TaskAction,

        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'Specify the full path for the executable or script (e.g., Powershell.exe or script path).',
            Position = 2)]
        [ValidateNotNullOrEmpty()]
        [string]
        $ActionPath,

        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'Specify the gMSA account that will run the task.',
            Position = 3)]
        [ValidateNotNullOrEmpty()]
        $gMSAAccount,

        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'Optional: Provide a description for the scheduled task.',
            Position = 4)]
        [string]
        $Description = 'Scheduled task created using gMSA.',

        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'Specify the type of task trigger: Daily or Weekly.',
            Position = 5)]
        [ValidateSet('Daily', 'Weekly')]
        [string]
        $TriggerType,

        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'Specify the time the task should run (HH:mm format).',
            Position = 6)]
        [ValidatePattern('^([01]?[0-9]|2[0-3]):[0-5][0-9]$', ErrorMessage = 'Time must be in HH:mm format.')]
        [string]
        $StartTime,

        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'For weekly tasks, specify the days of the week.',
            ParameterSetName = 'WeeklyTrigger',
            Position = 7)]
        [ValidateSet('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')]
        [string[]]
        $DaysOfWeek,

        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'For daily tasks, specify how many times per day the task should run. Acceptable values: 1, 2, 3, 4, 6, 8, 12, 24, 48.',
            ParameterSetName = 'DailyTrigger',
            Position = 8)]
        [ValidateSet(1, 2, 3, 4, 6, 8, 12, 24, 48)]
        [PSDefaultValue(Help = 'Default Value is "1"')]
        [int]
        $TimesPerDay = 1
    )

    begin {
        # Initial verbose message
        Write-Verbose -Message ('
            Starting the process to create the
                scheduled task {0}
                using gMSA {1}.'
 -f
            $PSBoundParameters['TaskName'], $PSBoundParameters['gMSAAccount']
        )


        # parameters variable for splatting CMDlets
        [hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase)

        #$triggerList = [System.Collections.Generic.List[object]]::New()
        $triggerList = @()

        # Validate if the gMSA account exists
        $gMSAAccount = Get-AdObjectType -Identity $PSBoundParameters['gMSAAccount']

        # Prepare the action
        $action = New-ScheduledTaskAction -Execute $PSBoundParameters['ActionPath'] -Argument $PSBoundParameters['TaskAction']
        Write-Verbose -Message ('
            Scheduled task action prepared:
                Action Path: {0}
                Task Action: {1}'
 -f
            $PSBoundParameters['ActionPath'], $PSBoundParameters['TaskAction']
        )
    } #end begin

    process {

        # Process the trigger based on TriggerType
        switch ($PSBoundParameters['TriggerType']) {
            'Weekly' {
                if (-not $PSBoundParameters['DaysOfWeek']) {
                    Write-Error -Message 'For a weekly task, you must specify at least one day of the week.'
                    return
                } #end If

                $NewTime = @{}

                If ($PSBoundParameters['StartTime'] -contains ':') {
                    $NewTime.Add('Hour', $PSBoundParameters['StartTime'].Split(':')[0])
                    $NewTime.Add('Minute', $PSBoundParameters['StartTime'].Split(':')[1])
                } else {
                    $NewTime.Add('Hour', $PSBoundParameters['StartTime'])
                }

                $Splat = @{
                    Weekly      = $true
                    DaysOfWeek  = $PSBoundParameters['DaysOfWeek']
                    At          = (Get-Date @NewTime).AddDays(1)
                    RandomDelay = (New-TimeSpan -Minutes 15)
                }
                $trigger = New-ScheduledTaskTrigger @Splat
                $triggerList += $trigger

                Write-Verbose -Message ('
                    Weekly trigger
                        created at {0}
                        on {1}.'
 -f
                    $PSBoundParameters['StartTime'], ($PSBoundParameters['DaysOfWeek'] -join ', ')
                )

            } #end Weekly

            'Daily' {
                $triggerList = @()
                $intervalMinutes = [math]::Round(1440 / $PSBoundParameters['TimesPerDay'])  # 1440 minutes in a day

                for ($i = 0; $i -lt $PSBoundParameters['TimesPerDay']; $i++) {
                    # Calculate the start time for each occurrence
                    $Splat = @{
                        Hour   = ([int]$PSBoundParameters['StartTime'].Split(':')[0])
                        Minute = ([int]$PSBoundParameters['StartTime'].Split(':')[1])
                    }
                    $currentTriggerTime = (Get-Date @Splat).AddDays(1).AddMinutes($i * $intervalMinutes)

                    $Splat = @{
                        Daily       = $true
                        At          = $currentTriggerTime
                        RandomDelay = (New-TimeSpan -Minutes 15)
                    }
                    $trigger = New-ScheduledTaskTrigger @Splat
                    $triggerList += $trigger

                    Write-Verbose -Message ('Daily trigger created at "{0}".' -f $currentTriggerTime)
                } #end for
            } #end Daily

        } #end switch

        if ($PSCmdlet.ShouldProcess("Scheduled Task: $PSBoundParameters['TaskName']")) {

            try {

                # Create the task principal with gMSA account
                $Splat = @{
                    UserId    = '{0}\{1}' -f $env:USERDOMAIN, $gMSAAccount.SamAccountName
                    LogonType = 'Password'
                    RunLevel  = 'Highest'
                }
                $principal = New-ScheduledTaskPrincipal @Splat
                Write-Verbose -Message ('Scheduled task principal created for gMSA {0}.' -f $gMSAAccount)

                # Register the task
                $Splat = @{
                    AllowStartIfOnBatteries    = $true
                    Compatibility              = 'Win8'
                    DontStopIfGoingOnBatteries = $true
                    Hidden                     = $true
                    Priority                   = 6
                    StartWhenAvailable         = $true
                }
                $taskSettings = New-ScheduledTaskSettingsSet @Splat

                $Splat = @{
                    TaskName  = $PSBoundParameters['TaskName']
                    Action    = $action
                    Trigger   = $triggerList
                    Principal = $principal
                    Settings  = $taskSettings
                }
                If ( $PSBoundParameters.ContainsKey('Description')) {
                    $Splat.Add('Description', $PSBoundParameters['Description'])
                }
                $task = Register-ScheduledTask @Splat

                # Set the author
                $Task.Author = $env:USERNAME
                $Task | Set-ScheduledTask

                Write-Verbose -Message ('
                    Scheduled task {0}
                    successfully created with gMSA {1}.'
 -f
                    $PSBoundParameters['TaskName'], $gMSAAccount
                )

                Write-Output $task
            } catch {
                Write-Error -Message ('Failed to create the scheduled task: {0}' -f $_)
            } #end try-catch
        } #end if
    } #end process

    end {
        Write-Verbose -Message ('Scheduled task creation process completed for {0}.' -f $PSBoundParameters['TaskName'])
    } #end end
} #end Function


# Example Usage:
# For weekly task:
# New-ScheduledTaskWithGMSA -TaskName "WeeklyTask" -TaskAction "-File C:\Scripts\MyScript.ps1" -ActionPath "Powershell.exe" -gMSAAccount "gmsaAccount$" -TriggerType "Weekly" -StartTime "09:00" -DaysOfWeek Monday,Wednesday,Friday

# For daily task (4 times per day):
# New-ScheduledTaskWithGMSA -TaskName "DailyTask" -TaskAction "-File C:\Scripts\MyScript.ps1" -ActionPath "Powershell.exe" -gMSAAccount "gmsaAccount$" -TriggerType "Daily" -StartTime "09:00" -TimesPerDay 4