Functions/Public/Push-LocalScheduledTask.ps1

#!/usr/bin/env pwsh

<#
.SYNOPSIS
    Deploy a local scheduled task to a remote machine
 
.DESCRIPTION
    This function exports the XML for a local scheduled task and creates that task on a remote machine.
 
.PARAMETER ComputerName
    This parameter specifies the remote host(s) to create the task(s) on. This parameter supports tab-completion based on the current domain.
    I can type '-ComputerName cgo-2' and this will tab-complete computer objects in the current domain that match the string 'cgo-2'
 
.PARAMETER TaskName
    This parameter specifies the local task name to export and create on a remote machine. I have not tested how to export nested tasks (See Register-ScheduledTask '-TaskPath' parameter)
    You can specify multiple task names, separated by comma please.
 
.PARAMETER Credential
    Specifies a user account that has permission to perform this action. The default is the current user.
    This credential is used for a task against the local pc, and the remote PC. because of this, the account used must have rights to do the required tasks on all computers involved.
 
    Type a user name, such as User01 or Domain01\User01 , or enter a PSCredential object generated by
    the `Get-Credential` cmdlet. If you type a user name, you're prompted to enter the password.
 
    Credentials are stored in a PSCredential
    (/dotnet/api/system.management.automation.pscredential)object and the password is stored as a
    SecureString (/dotnet/api/system.security.securestring).
 
    > [!NOTE] > For more information about SecureString data protection, see > How secure is
    SecureString? (/dotnet/api/system.security.securestring#how-secure-is-securestring).
 
.PARAMETER Force
    Instructs the cmdlet to perform the operation without prompting for confirmation.
    Additionally this will overwrite any remote tasks with the same name.
    If you attempt to deploy a local task on a remote machine without using -Force, the export will fail.
 
.PARAMETER PassThru
    I haven't gotten this switch to be accurate. Currently this function will ALWAYS spit out Scheduled Task objects that it creates whether -PassThru is present or not.
    The goal is to have the function not return objects if -PassThru is not present... like every other advanced function.
 
.EXAMPLE
    PS>Deploy-LocalScheduledTask -ComputerName Computer1,Computer2 -TaskName "Task1","Task2"
 
    Description
    -----------
    This will export both Task1 and Task2 scheduled tasks to both Computer1 and Computer2.
 
.EXAMPLE
    PS>Deploy-LocalScheduledTask -ComputerName Computer1 -TaskName "Task1" -Credential (Get-Credential) -Force
 
    Description
    -----------
    This will export the task Task1 to Computer1 using the provided credentials. You can also save the results of Get-Credential to a variable and use the '-Credential $cred' method.
    This will also overwrite a possible existing task named Task1 since -Force is used.
 
.NOTES
    Author: Matthew J. DeGarmo
    Handle: @matthewjdegarmo
#>

function Push-LocalScheduledTask() {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string[]] $ComputerName,

        [Parameter(Mandatory)]
        [string[]] $TaskName,

        [PSCredential] $Credential,

        [switch] $Force,

        #? Should this switch just be tossed?
        #TODO This switch doesn't do anything. Need to figure out how to supress Invoke-Command output for Register-ScheduledTask command.
        [switch] $PassThru
    )

    begin {
        $ComputerName = foreach ($Computer in $ComputerName) {
            if (Test-Connection -ComputerName $Computer -Count 1 -ErrorAction SilentlyContinue) {
                $Computer
            }
            else {
                Write-Warning "Test-Connection to '$ComputerName' failed."
            }
        }
    }

    process {
        try {
            foreach ($Computer in $ComputerName) {
                $InvokeParams = @{
                    ComputerName = $Computer
                }
                if ($PSBoundParameters.ContainsKey('Credential')) { $InvokeParams += @{Credential = $Credential } }
                
                foreach ($Task in $TaskName) {
                    if ($PSBoundParameters.ContainsKey('Credential')) {
                        $TaskXML = Invoke-Command -ComputerName $env:COMPUTERNAME -Credential $Credential -Command { Export-ScheduledTask $using:Task | Out-String }
                    }
                    else {
                        $TaskXML = Export-ScheduledTask $Task | Out-String
                    }

                    $TaskParams = @{
                        Xml      = $TaskXML
                        TaskName = $Task
                    }
                    if ($PSBoundParameters.ContainsKey('Force')) { $TaskParams += @{Force = $true } }
                    if (-Not($PSBoundParameters.ContainsKey('PassThru'))) { $TaskParams += @{InformationAction = 'Ignore' } }

                    Invoke-Command @InvokeParams -ScriptBlock {
                        Register-ScheduledTask @using:TaskParams
                    }
                }
            }
        }
        catch {
            Write-Error "$($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
        }
    }

    end {}
}