If you run any scheduled tasks in Windows that rely on an account that must change passwords regularly (which, frankly, every account should at least yearly…), it’s convenient to update the passwords in the least obnoxious way possible.

I’ve recently had reason to perform this particular task, and after updating 8 scheduled tasks across 7 servers via the GUI, I knew there had to be a better way.  And, as always, that better way is with PowerShell.  This appears to be only viable for Windows 10 and Windows Server 2016, but if you’re not using those…please get moving.  It’s 2018, Server 2019 has reached GA, and it’s time to upgrade or replace your old servers.  Yes, 2012 R2 is old.  Get over it.

$TaskCredential = Get-Credential
Get-ScheduledTask | Where-Object { $_.Principal.UserId -eq $TaskCredential.UserName } | Set-ScheduledTask -User $TaskCredential.UserName -Password $TaskCredential.GetNetworkCredential().Password

Simple as that – any scheduled tasks that are being run as the account provided will have their password updated to the new password.  Easy peasy.  But…this requires that you are running the command on the system you want to update?  What about running it remotely?  I’m glad you asked…

Param(
  [Parameter(Mandatory=$true,ValueFromPipeline=$true)][string[]]$ComputerName,
  [Parameter(Mandatory=$true)][PSCredential]$Credential,
  [Parameter(Mandatory=$true)][PSCredential]$TaskCredential
)

Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock { Get-ScheduledTask | Where-Object { $_.Principal.UserId -eq ($using:TaskCredential).UserName.Split('\')[1] } | Set-ScheduledTask -User ($using:TaskCredential).UserName -Password ($using:TaskCredential).GetNetworkCredential().Password }

With a little bit of coaxing, you can pass a PSCredential directly through Invoke-Command, which then is used to update the password on the tasks. All you need to provide is the list of computers, credentials to login to those computers, and the credentials you want to update tasks for.

Behold the power of PowerShell.

15 Comments

  1. The first credential dialog is to log into the remote computer and the second credential dialog is for accessing the account on the Task Scheduler task, so where is the new password sourced from?

    1. The second credential is the name/password to run the task as. That’s where the password comes from.
      The Set-ScheduledTask piece of the command pulls the username and password out of the $TaskCredential object to use for the task.
      If you don’t see how that’s happening, look into the using: namespace with Invoke-Command (Example 9 on this page: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-7)

  2. I am looking to integrate this into another script where I am changing a password for a user that has Services, Scheduled Tasks and COM+ Applications, does it matter if I change the variable names? I have included my adjusted version of your script.

    Param(
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)][string[]]$Server,
    [Parameter(Mandatory=$true)][PSCredential]$Credential,
    [Parameter(Mandatory=$true)][PSCredential]$Password
    )

    Invoke-Command -ComputerName $Server -Credential $Credential -ScriptBlock { Get-ScheduledTask | Where-Object { $_.Principal.UserId -eq ($using:Password).UserName.Split(‘\’)[1] } | Set-ScheduledTask -User ($using:Password).UserName -Password ($using:Password).GetNetworkCredential().Password }

    1. No, changing the variable names doesn’t matter. It looks like all you’ve changed was the name of the variables, so everything should continue to work as expected.

  3. I’m confused. I could get your first set of code to work on the server locally. But I couldn’t get your remote code to work at all. It wasn’t returning any errors but it wasn’t returning any results either. Then I noticed your remote code is using a .split on the first cred where your local code does not.

    As soon as removed the .split….. it worked!

    Also since my servers and the tasks in question are the same creds:

    Param(
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)][string[]]$ComputerName,
    [Parameter(Mandatory=$true)][PSCredential]$Credential
    )

    Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock { Get-ScheduledTask | Where-Object { $_.Principal.UserId -eq ($using:Credential).UserName } | Set-ScheduledTask -User ($using:Credential).UserName -Password ($using:Credential).GetNetworkCredential().Password }

    Many thanks for this! It will certainly save a ton of time in the future.

  4. This is great read,but I get access denied .Is there anything i can do ?Set-ScheduledTask : Access is denied.

    1. It seems like your user ID doesn’t have access to modify the scheduled task.
      First step in troubleshooting is to try to use Set-ScheduledTask while locally logged in. My guess is that won’t work. You’d have to troubleshoot that, then move on to the remote command.

  5. Thank you for the script. It works on me!

    I run a schedule on a few computers, but I do not remember which computer I scheduled with that user id.
    Is their way of modifying this script to go and search on all AD- computers like Get-ADComputer-Filter instead of me entering the computer name?

    1. You don’t need to modify the script at all. The Parameter block will accept pipeline input and assume it’s a list of computer names. Just do your Get-ADComputer command with whatever filter you need, pipe it to select just the computer name, then pipe it into the script, like this:
      Get-ADComputer -Filter “Name -like ‘cert*'” | Select-Object Name | MyScript.ps1 -Credential $Cred -TaskCredential $TaskCred

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.