Last active
February 11, 2025 14:59
-
-
Save Bill-Stewart/a0af19baac4638a852d6be9cae5f36c9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Rename-ScheduledTask.ps1 | |
# Written by Bill Stewart (bstewart AT iname.com) | |
#requires -version 3 | |
<# | |
.SYNOPSIS | |
Renames a scheduled task on a computer by copying an existing task to a new task, then deleting the original task. | |
.DESCRIPTION | |
Renames a scheduled task on a computer by copying an existing task to a new task, then deleting the original task. If you rename a scheduled task that has saved credentials, you must respecify the task's credentials. | |
.PARAMETER TaskName | |
The scheduled task you want to rename. If you don't specify a folder name, the root folder ("\") is the default. If the task has saved credentials, you will be prompted for the task's credentials if you don't specify -TaskCredential. | |
.PARAMETER NewName | |
The new name for the task. You can specify a new folder name for the task. If you don't specify a different folder name, the default is to save the new task in the same folder as the original task. If the task has saved credentials, you will be prompted for the task's credentials if you don't specify -TaskCredential. If the folder name does not exist, it will be created. | |
.PARAMETER ComputerName | |
The computer on which the task exists. The current computer is the default. | |
.PARAMETER ConnectionCredential | |
The connection to the task scheduler service will be made using these credentials. If you don't specify this parameter, the currently logged on user's credentials are assumed. | |
.PARAMETER TaskCredential | |
If the scheduled task you are renaming has saved credentials, use these credentials when creating the new task. If the scheduled task has saved credentials and you don't specify this parameter, you will be prompted for new credentials. | |
.EXAMPLE | |
PS C:\> Rename-ScheduledTask "Scheduled Task 0" "Scheduled Task 1" | |
This command renames \Scheduled Task 0 to \Scheduled Task 1. | |
.EXAMPLE | |
PS C:\> Rename-ScheduledTask \TaskFolder1\Task0 Task1 | |
This command renames \TaskFolder1\Task0 to \TaskFolder1\Task1. | |
.EXAMPLE | |
PS C:\> Rename-ScheduledTask \TaskFolder0\MyTask1 \TaskFolder1\MyTask1 | |
This command renames \TaskFolder0\MyTask1 to \TaskFolder1\MyTask1. | |
.EXAMPLE | |
PS C:\> Rename-ScheduledTask MessageTask0 TestMessage -ComputerName server1 | |
This command renames MessageTask0 to TestMessage on server1. | |
.EXAMPLE | |
PS C:\> Rename-ScheduledTask Test0 Test1 -ComputerName server1 -ConnectionCredential (Get-Credential) | |
This command renames \Task0 to \Test1 on server1. The connection to server1 is established using prompted credentials. | |
.EXAMPLE | |
PS C:\> Rename-ScheduledTask FileCopy1 FileCopy2 -ComputerName server2 | |
If the task File1Copy1 on server2 has saved credentials, this command will prompt for credentials when renaming FileCopy1 to FileCopy2. | |
.EXAMPLE | |
PS C:\> Rename-ScheduledTask FileCopy1 FileCopy2 -ComputerName server2 -TaskCredential $Cred | |
If the task File1Copy1 on server2 has saved credentials, this command will use the credentials in the $Cred variable when renaming FileCopy1 to FileCopy2. | |
#> | |
param( | |
[Parameter(Position = 0,Mandatory)] | |
[String] | |
$TaskName, | |
[Parameter(Position = 1,Mandatory)] | |
[String] | |
$NewName, | |
[String] | |
$ComputerName, | |
[Management.Automation.PSCredential] | |
$ConnectionCredential, | |
[Management.Automation.PSCredential] | |
$TaskCredential | |
) | |
$TASK_ILLEGAL_CHARS = @('/',':','*','?','"','<','>','|') | |
$TASK_CREATE = 2 | |
$TASK_LOGON_PASSWORD = 1 | |
if ( -not $ComputerName ) { | |
$ComputerName = [Net.Dns]::GetHostName() | |
} | |
# Returns whether the specified task name is valid | |
function Test-ValidTaskName { | |
param( | |
[String] | |
$taskName | |
) | |
foreach ( $char in $taskName.ToCharArray() ) { | |
if ( $TASK_ILLEGAL_CHARS -contains $char ) { | |
return $false | |
} | |
} | |
$true | |
} | |
# Returns $true if the specified user ID is a gMSA | |
function Test-ServiceAccount { | |
param( | |
[String] | |
$userId | |
) | |
try { | |
$sid = ([Security.Principal.NTAccount] $userId).Translate([Security.Principal.SecurityIdentifier]) | |
$dirEntry = [ADSI] ("LDAP://<SID={0}>" -f $sid.Value) | |
$dirEntry.objectClass -contains "msDS-GroupManagedServiceAccount" | |
} | |
catch { | |
return $false | |
} | |
} | |
function ConvertTo-String { | |
param( | |
[Security.SecureString] | |
$secureString | |
) | |
try { | |
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString) | |
[Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) | |
} | |
finally { | |
if ( $bstr -ne [IntPtr]::Zero ) { | |
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) | |
} | |
} | |
} | |
# Returns a list of all tasks starting at the specified task folder | |
function Get-TaskName { | |
param( | |
$taskFolder | |
) | |
$tasks = $taskFolder.GetTasks(0) | |
if ( $null -ne $tasks ) { | |
$tasks | ForEach-Object { $_.Path } | |
$taskFolders = $taskFolder.GetFolders(0) | |
$taskFolders | ForEach-Object { Get-TaskName $_ } | |
} | |
} | |
if ( -not (Test-ValidTaskName $NewName) ) { | |
throw "Task name cannot contain any of the following characters: $TASK_ILLEGAL_CHARS" | |
} | |
# Assume root folder if not specified | |
if ( -not $TaskName.Contains("\") ) { | |
$TaskName = "\$TaskName" | |
} | |
# If new name specified without folder name, assume same folder as original | |
if ( -not $NewName.Contains("\") ) { | |
$NewName = Join-Path (Split-Path $TaskName -Parent) $NewName | |
} | |
# Throw an error if the original and new names are the same | |
if ( $TaskName -eq $NewName ) { | |
throw "-TaskName and -NewName parameters cannot specify the same name." | |
} | |
# Assume $null for the schedule service connection parameters unless | |
# -ConnectionCredential used | |
$UserName = $DomainName = $ConnectPwd = $null | |
if ( $ConnectionCredential ) { | |
if ( $ConnectionCredential.UserName.Contains("\") ) { | |
$UserName = $ConnectionCredential.UserName.Split("\")[1] | |
$DomainName = $ConnectionCredential.UserName.Split("\")[0] | |
} | |
else { | |
$UserName = $ConnectionCredential.UserName | |
$DomainName = $env:USERDOMAIN | |
} | |
$ConnectPwd = ConvertTo-String $ConnectionCredential.Password | |
} | |
# Try to create the TaskService object | |
try { | |
$TaskService = New-Object -ComObject "Schedule.Service" | |
} | |
catch [Management.Automation.PSArgumentException] { | |
throw $_ | |
} | |
# Try to connect to the schedule service on the computer | |
try { | |
$TaskService.Connect($ComputerName,$UserName,$DomainName,$ConnectPwd) | |
} | |
catch [Management.Automation.MethodInvocationException],[Runtime.InteropServices.COMException] { | |
throw $_ | |
} | |
# Get a reference to the root schedule folder | |
$RootFolder = $TaskService.GetFolder("\") | |
# Retrieve a list of all registered tasks' names | |
$TaskNames = Get-TaskName $RootFolder | |
# Throw an error if we can't find the task | |
if ( -not ($TaskNames -contains $TaskName) ) { | |
throw "Scheduled task '$TaskName' not found on computer '$ComputerName'." | |
} | |
# Throw an error if the new task name already exists | |
if ( $TaskNames -contains $NewName ) { | |
throw "Scheduled task '$NewName' already exists computer '$ComputerName'." | |
} | |
# Get the TaskDefinition object for the original task | |
$TaskDefinition = $RootFolder.GetTask($TaskName).Definition | |
# Task needs a password if: | |
# * LogonType = TASK_LOGON_PASSWORD AND | |
# * UserId does NOT specify a gMSA | |
$TaskPwd = $null | |
if ( ($TaskDefinition.Principal.LogonType -eq $TASK_LOGON_PASSWORD) -and | |
(-not (Test-ServiceAccount $TaskDefinition.Principal.UserId)) ) { | |
# If -TaskCredential not specified, prompt for credentials | |
if ( -not $TaskCredential ) { | |
$TaskCredential = $Host.UI.PromptForCredential("Task Credentials", | |
"Please specify credentials for the scheduled task.", | |
"$env:USERDOMAIN\$env:USERNAME", | |
"") | |
if ( -not $TaskCredential ) { | |
throw "You must specify credentials." | |
} | |
} | |
$TaskPwd = ConvertTo-String $TaskCredential.Password | |
} | |
try { | |
# Create new task as copy of original (void cast prevents output) | |
[Void] $RootFolder.RegisterTaskDefinition($NewName, | |
$TaskDefinition, | |
$TASK_CREATE, | |
$TaskDefinition.Principal.UserId, | |
$TaskPwd, | |
$TaskDefinition.Principal.LogonType) | |
# Delete the original task | |
$TaskFolder = $TaskService.GetFolder((Split-Path $TaskName -Parent)) | |
$TaskFolder.DeleteTask((Split-Path $TaskName -Leaf),$null) | |
} | |
catch [Management.Automation.MethodInvocationException] { | |
throw $_ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment