New-ScheduledScript.ps1


<#PSScriptInfo
 
.VERSION 1.0
 
.GUID 1bb88c7e-f564-44b9-b3af-37936a4f9aec
 
.AUTHOR Adam Bertram
 
.COMPANYNAME Adam the Automator, LLC
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
.PRIVATEDATA
 
#>
 



<#
.DESCRIPTION
    A PowerShell script to create a scheduled task on a remote computer that invokes a PowerShell script.
 
.NOTES
    Created on: 1/20/15
    Created by: Adam Bertram
    Filename: New-ScheduledScript.ps1
    Credits: http://blogs.technet.com/b/heyscriptingguy/archive/2015/01/16/use-powershell-to-create-scheduled-task-in-new-folder.aspx
.EXAMPLE
    PS> New-ScheduledScript.ps1 -ScriptFilePath C:\callfromvbs.ps1 -LocalScriptFolderPath 'C:\' -TaskTriggerOptions @{'Daily' = $true; 'At' = '3Am'} -TaskName 'Test' -TaskRunAsUser 'lab.local\administrator' -TaskRunAsPassword 'mypassword' -Computername labdc.lab.local
     
    This script would copy the C:\callfromvbs.ps1 to the C:\ of labdc.lab.local. It would then create a scheduled task on labdc.lab.local
    named Test that ran daily at 3AM under the lab.local\administrator credentials.
.PARAMETER ScriptFilePath
    The remote or local file path of the Powershell script
.PARAMETER ScriptParameters
    Any parameters to execute with the script
.PARAMETER LocalScriptFolderPath
    If this script is copying a Powershell script from somewhere else, this is the folder path where the
    script will be copied to and referenced to run in the scheduled task.
.PARAMETER TaskTriggerOptions
    A hashtable of parameters that will be passed to the New-ScheduledTaskTrigger cmdlet. For available options, visit
    http://technet.microsoft.com/en-us/library/jj649821.aspx.
.PARAMETER TaskName
    The name of the scheduled task
.PARAMETER TaskRunAsUser
    The username that the task will run under
.PARAMETER TaskRunAsPassword
    The password to the runas user
.PARAMETER Computername
    The name of the system in which the scheduled task will be created (if not the local system)
.PARAMETER PowershellRunAsArch
    If your task scheduler machine is 64 bit this enforces the script to run under the 32 bit or 64 bit
    Powershell host. By default, it will always run as 64 bit.
#>

[CmdletBinding()]
[OutputType([bool])]
param (
    [Parameter(Mandatory,
        ValueFromPipeline,
        ValueFromPipelineByPropertyName)]
    [ValidatePattern('.*\.ps1$')]
    [string]$ScriptFilePath,
    [string]$ScriptParameters,
    [Parameter(Mandatory)]
    [string]$LocalScriptFolderPath,
    [Parameter(Mandatory)]
    [hashtable]$TaskTriggerOptions,
    [Parameter(Mandatory)]
    [string]$TaskName,
    [Parameter(Mandatory)]
    [string]$TaskRunAsUser,
    [Parameter(Mandatory)]
    [string]$TaskRunAsPassword,
    [ValidateScript({ Test-Connection -ComputerName $_ -Quiet -Count 1})]
    [string]$Computername = 'localhost',
    [ValidateSet('x86', 'x64')]
    [string]$PowershellRunAsArch
)

begin {
    $ErrorActionPreference = 'Stop'
    Set-StrictMode -Version Latest
    ## http://www.leeholmes.com/blog/2009/11/20/testing-for-powershell-remoting-test-psremoting/
    function Test-PsRemoting {
        param (
            [Parameter(Mandatory)]
            $computername
        )
        
        try {
            $errorActionPreference = "Stop"
            $result = Invoke-Command -ComputerName $computername { 1 }
        } catch {
            return $false
        }
        
        ## I’ve never seen this happen, but if you want to be
        ## thorough….
        if ($result -ne 1) {
            Write-Verbose "Remoting to $computerName returned an unexpected result."
            return $false
        }
        $true
    }
    
    function Get-ComputerArchitecture {
        if ((Get-CimInstance -ComputerName $Computername Win32_ComputerSystem -Property SystemType).SystemType -eq 'x64-based PC') {
            'x64'
        } else {
            'x86'    
        }
    }
    
    function Get-PowershellFilePath {
        ## If user wants to run the script under the x86 host and the machine is x64
        if (($PowershellRunAsArch -eq 'x86') -and ((Get-ComputerArchitecture) -eq 'x64')) {
            if ($Computername -eq 'localhost') {
                ## If it's the localhost no need to do a WinRM query
                "$($PsHome.Replace('System32','SysWow64'))\powershell.exe"
            } else {
                Invoke-Command -ComputerName $Computername -ScriptBlock { "$($PsHome.Replace('System32','SysWow64'))\powershell.exe" }
            }
        } else {
            ## If the machine is 32 bit anyway or if it's 64 bit and the user wants 64 bit just use $PsHome
            if ($Computername -eq 'localhost') {
                ## If it's the localhost no need to do a WinRM query
                "$PsHome\powershell.exe"
            } else {
                Invoke-Command -ComputerName $Computername -ScriptBlock { "$PsHome\powershell.exe" }
            }
        }
    }
    
    function New-MyScheduledTask {
        try {
            $PowershellFilePath = Get-PowershellFilePath
            if ($Computername -ne 'localhost') {
                $ScriptBlock = {
                    $Action = New-ScheduledTaskAction -Execute $using:PowershellFilePath -Argument "-NonInteractive -NoLogo -NoProfile -File $using:ScriptFilePath $using:ScriptParameters"
                    $TaskTriggerOptions = $using:TaskTriggerOptions
                    $Trigger = New-ScheduledTaskTrigger @TaskTriggerOptions
                    $RunAsUser = $using:TaskRunAsUser
                    $Task = New-ScheduledTask -Action $Action -Trigger $Trigger -Settings (New-ScheduledTaskSettingsSet);
                    Register-ScheduledTask -TaskName $using:TaskName -InputObject $Task -User $using:TaskRunAsUser -Password $using:TaskRunAsPassword
                }
                $Params = @{
                    'Scriptblock'  = $ScriptBlock
                    'Computername' = $Computername
                }
                Invoke-Command @Params
            } else {
                $Action = New-ScheduledTaskAction -Execute $PowershellFilePath -Argument "-NonInteractive -NoLogo -NoProfile -File $ScriptFilePath"
                $Trigger = New-ScheduledTaskTrigger @TaskTriggerOptions
                $Task = New-ScheduledTask -Action $Action -Trigger $Trigger -Settings (New-ScheduledTaskSettingsSet);
                Register-ScheduledTask -TaskName $TaskName -InputObject $Task -User $TaskRunAsUser -Password $TaskRunAsPassword
            }
        } catch {
            Write-Error "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
            $false
        }
    }
    
    function Get-UncPath ($HostName, $LocalPath) {
        $NewPath = $LocalPath -replace (":", "$")
        if ($NewPath.EndsWith("\")) {
            $NewPath = [Regex]::Replace($NewPath, "\\$", "")
        }
        "\\$HostName\$NewPath"
    }
    
    try {
        if (($Computername -ne 'localhost') -and !(Test-PsRemoting -computername $Computername)) {
            throw "PS remoting not available on the computer $Computername"
        }
        ## Ensure there's no trailing slash
        $LocalScriptFolderPath = $LocalScriptFolderPath.TrimEnd('\')
    } catch {
        Write-Error "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
        exit
    }
}

process {
    try {
        ## If we're not creating a task on the local computer or if the script to execute is not on the local computer
        ## copy to the task scheduler system and change the script to execute to a local path
        if ($ScriptFilePath.StartsWith('\\') -or ($Computername -ne 'localhost')) {
            Copy-Item -Path $ScriptFilePath -Destination (Get-UncPath -HostName $Computername -LocalPath $LocalScriptFolderPath)
            $ScriptFilePath = "$LocalScriptFolderPath\$($ScriptFilePath | Split-Path -Leaf)"
        }
        New-MyScheduledTask | Out-Null
        $true
    } catch {
        Write-Error "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
        $false
    }
}