Despite multiple articles on that in the blogosphere already I keep getting questions on running PowerShell as Windows scheduled tasks – so here’s a quick summary what I see as the way to do this (assuming that you are running PowerShell 2.0). I hope you learn something new.
1. Get your script ready
Surprising as it might sound, your script might actually not be ready to run in a scheduled task as is. This happens if it uses cmdlets from a particular PowerShell module or snapin, and it worked for you interactively because you used a specialized shell (e.g. Exchange Management Shell) or a tool like PowerGUI Script Editor which loads the modules for you.
If you indeed are using using any non-default cmdlets, simply add Add-PSSnapin or Import-Module to the beginning of the script. For example:
Add-PSSnapin Quest.ActiveRoles.ADManagement
2. Schedule the task
To schedule a task simply start Windows Task Scheduler and schedule powershell.exe executable passing the script execution command as a parameter. The -File
parameter is the default one so simply specifying the script path as the argument would work in a lot of cases:
You can find powershell.exe in your system32\WindowsPowerShell\v1.0 folder.
4. Report task success or failure
If you want your script to report success or failure (or some sort of other numerical result) simply use the exit keyword in the script to pass the value, e.g.:
exit 4
Then your Windows Task Scheduler will show the value in the Last Run Result (you might need to hit F5 to refresh the column in the task scheduler):
3. Passing parameters
If you need to pass parameters things get a little trickier. Say, you have a script which adds two numbers:
param($a=2, $b=2) "Advanced calculations ahead" exit $a + $b
To pass the numbers as parameters, you would want to use powershell.exe -Command
instead of powershell.exe -File
. This -Command
argument will then have the script invocation operator &
, path to the script, and the parameters. E.g.:
Program: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Add argument (optional): -Command "& c:\scripts\hello.ps1 -a 2 -b 3"
If you want to also get your exit code from the script, you would need to re-transmit that by adding exit $LASTEXITCODE
to the command (I learnt this tip from MoW). E.g.
Program: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Add argument (optional): -Command "& c:\scripts\hello.ps1 -a 2 -b 3; exit $LASTEXITCODE"
5. Run x86 PowerShell on x64 Windows
On 64-bit versions of Windows you actually have both 64-bit and 32-bit versions of PowerShell. In most cases you don’t care but in some cases (e.g. specific COM objects being used) you might need specifically a 32-bit version. To get that to run, simply pick the proper executable when you schedule the task:
Regular PowerShell (64-bit version on 64-bit Windows): %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe
32-bit PowerShell (x86): %SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe
6. Other options
To learn about all parameters PowerShell executable has simply run it with /? option (from either cmd.exe or a PowerShell session).
I normally use -noprofile to make sure that nothing in the PowerShell profile interferes with the task.
Also, if your Execution Policy does not allow running scripts the -ExecutionPolicy parameter comes handy allowing you to make an exception just for this task. E.g.:
c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -File c:\scripts\hello.ps1 -ExecutionPolicy RemoteSigned
Here are some other parameters provided by PowerShell:
-PSConsoleFile Loads the specified Windows PowerShell console file. To create a console file, use Export-Console in Windows PowerShell.
I guess you could use that is you want the exact environment you have in the predefined shell from Exchange, AD, or SQL. E.g.: PowerShell -PSConsoleFile SqlSnapIn.Psc1
-Version Starts the specified version of Windows PowerShell.
I don’t think this one actually works.
-NoLogo
Hides the copyright banner at startup.
Not really relevant for scheduled tasks, imho…
-NoExit Does not exit after running startup commands.
Might be useful for troubleshooting.
-Sta Start the shell using a single-threaded apartment.
If your script needs STA mode (if you don’t know what this is – most likely you don’t need this. ;))
-NonInteractive Does not present an interactive prompt to the user.
Not really relevant for scheduled tasks, imho…
-InputFormat Describes the format of data sent to Windows PowerShell. Valid values are "Text" (text strings) or "XML" (serialized CLIXML format). -OutputFormat Determines how output from Windows PowerShell is formatted. Valid values are "Text" (text strings) or "XML" (serialized CLIXML format).
Not sure how I would use those… Here’s one example of how -InputFormat none
can help fix issues with PowerShell becoming unresponsive when waiting for input.
-WindowStyle Sets the window style to Normal, Minimized, Maximized or Hidden.
Unfortunately, I could not make this work. I tried to use -WindowStyle Hidden
to avoid the PowerShell console window popping up during task execution but with no luck.
-EncodedCommand Accepts a base-64-encoded string version of a command. Use this parameter to submit commands to Windows PowerShell that require complex quotation marks or curly braces. # To use the -EncodedCommand parameter: $command = 'dir "c:\program files" ' $bytes = [System.Text.Encoding]::Unicode.GetBytes($command) $encodedCommand = [Convert]::ToBase64String($bytes) powershell.exe -encodedCommand $encodedCommand
Can be useful when having to pass advanced expressions and getting issues with parser.
Any other aspects which I forgot to cover? Please leave your comments!
Dmitry
Just this morning I was fighting with this… I was trying to avoid using .bat files to call a .ps1 file… seemed silly.
Under Action I have “powershell.exe” in the program field, and “C:\SCRIPTS\View-DeleteUnusedADObjects.ps1 -noprofile –Noninteractive” in the Add Arguments section.
Why not just use the -file option to pass the script name to PowerShell?
Really, it’s not surprising there are people still asking questions about how to do this, with so many conflicting answers.
Nice summary article.
I agree with Bruce, explain the difference between -File and -Command. It’s not clear to many users.
Perhaps you could also add how to run a PS script in 32-bit mode on a 64-bit OS ?
Thanks guys. Please check out the updated text and let me know if there is anything still unclear or missing.
Good, now I will do it!
Thanks for the post.
“-WindowStyle Hidden”
Yes, there’s a basic flaw with how this works, especially when it is called in certain circumstances.
“Add argument (optional): -Command “& c:\scripts\hello.ps1 -a 2 -b 3; exit $LASTEXITCODE””
Yes, it is simpler if you do not have any spaces in the path, otherwise, I’ve had to revert to a combination of things like single quotes.
Marco,
I’m glad that I am not the only one for whom -WindowsStyle Hidden is not working (why do they even mention non-working parameters in the help?)
And, yes, I need to update the post to talk about how spaces, quotes, etc. can make the command-line more challenging… Will do… Good catch!
Dmitry
FYI, my solution to this problem was to run as “NT AUTHORITY\System”, so that the window doesn’t appear in the user’s session.
schtasks /CREATE /RU “NT AUTHORITY\SYSTEM” /XML $schtasksXmlLocation /TN $nameForTask /F
Dmitry,
I’m another one who hasn’t seen -WindowsStyle working so far Wrapper c# application solves it. However, it’s a shame that the -WindowsStyle param itself is useles.
stej
Bah! There isn’t even a Microsoft Connect bug or suggestion on this… I might argue it is more of a suggestion, but some may say bug… I’ll create a connect item tonight, and post back here with the URL.
And by “there isn’t a bug or suggestion on this”, I mean on “-WindowStyle Hidden”, not actually hiding the PowerShell console.
Hi Dmitry,
I have a Hyper-V Server 2008 R2 SP1 server running with a UPS connected to it. Since there isn’t really any UPS management app available to control the UPS under Hyper-V 2008 Server, I am using the following powershell script to send notification and shutdown the server when the battery reached critical state.
# Initialize Variables
# Shutdown threshold at 45% of remaining UPS capacity
$threshhold = 45
$interval = 60
$OnBattery = 0
$Event = 0
$Shutdown = 0
$hostname = hostname
# Create SMTP client
$Server = “smtp server”
$Port = 25
$Client = New-Object System.Net.Mail.SmtpClient $Server, $Port
$Client.Credentials = New-Object System.Net.NetworkCredential(“username”, “pwd”);
$To = “someone”
$From = “someone”
# Loop on Battery Query
while (1)
{
$bat = get-wmiobject -class CIM_Battery -namespace “root\CIMV2”
$batstatus = $bat.batterystatus
$batcapacity = $bat.estimatedchargeremaining
$timetoshutdown = $bat.estimatedruntime/2
if ($batstatus -eq 1)
{
$Event = 1
$OnBattery = 1
# “On Battery”
$Subject = “Utility Power Failure: {0} is running On UPS Battery” -f $hostname
$Body = “UPS at {0} % remaining capacity, approximately {1} minutes before {2} shutdown.” -f $batcapacity, $timetoshutdown, $hostname
if ($batcapacity -lt ($threshhold +5) )
{
$Body = “Shutdown imminent at {0} %, with ” -f $threshhold + $Body
}
if ($batcapacity -le $threshhold )
{
$Subject = “{0} Battery level is CRITICAL. {1} % remaining” -f $hostname, $batcapacity
$Body = “{0} is Shutting Down” -f $hostname
$Shutdown = 1
}
}
elseif (($batstatus -eq 2) -and ($OnBattery -eq 1))
{
$Event = 1
$OnBattery = 0
$Shutdown = 0
# “Power Restored”
$Subject = “Utility Power Restored to {0}.” -f $hostname
$Body = “Battery at {0} % capacity. UPS charging… ” -f $batcapacity
}
if ($Event -eq 1) # Create mail message
{
$Event = 0
$Message = New-Object System.Net.Mail.MailMessage $From, $To, $Subject, $Body
$Message.Priority = [System.Net.Mail.MailPriority]::High
try {
$Client.Send($Message)
# “Message sent successfully”
}
catch {
“Exception caught in UPS_Monitor.ps1”
}
}
If ($Shutdown -eq 1) #Shutdown Server
{
$win32OS = get-wmiobject win32_operatingsystem -computername .
$win32OS.psbase.Scope.Options.EnablePrivileges = $true
$win32OS.win32shutdown(1)
}
sleep $interval
}
The problem I am having is with the Shutdown. When Task Scheduler is running the script, nothing happens. When I execute the script manually, it shut down the server. Just wondering if you ever encounter something like this…
Thanks.
Minh,
1. I would first of all check to make sure that your scheduled task runs under the same account as the one under which you tested it.
2. If this does not help, try to just start cmd.exe and from there run powershell.exe yourscript.ps1.
3. If it works in cmd.exe but still fails in scheduled task – try to add this to the beginning of the script so you can then check the transcript and see how the script executed: Start-Transcript -Path c:\mydebug.log
Dmitry
Hi Dmitry,
1. The task is being executed as Administrator with highest priviliges. I know the script is running because when the UPS switches to battery. I get the email notification. When it hits the treshold, nothing happens.
2. I tried it this way and it works flawlessly. Email notifications are sent out when the UPS switches to battery and shutdown the server once the threshold is reached.
3. I will try this and you know.
I isolated the shutdown code and put it in shutdown.ps1 file.
$win32OS = get-wmiobject win32_operatingsystem -computername .
$win32OS.psbase.Scope.Options.EnablePrivileges = $true
$win32OS.win32shutdown(1)
Ran the script via cmd window and it works. Same script executed via Task Scheduler fails. Here’s the output of the debug file:
**********************
Windows PowerShell Transcript Start
Start time: 20110622173638
Username : SERVER\Administrator
Machine : SERVER (Microsoft Windows NT 6.1.7601 Service Pack 1)
**********************
Transcript started, output file is d:\mydebug.log
__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ReturnValue : 1191
**********************
Windows PowerShell Transcript End
End time: 20110622173638
**********************
I can’t find any references to ReturnValue: 1191. Not sure what that means.
Thanks for the heads up on that Transcript part. I have been looking for a simple all encompassing command to just pipe all script logging to a file.
Does anyone know if you can get the -args parameter to work with -encodedcommand ?
I’ve written a PS script to query either an ip or hostname and return AD info.
I want to wrap it in a .bat so that there’s a single file to distribute, not a batch file that calls a ps script.
I’ve done an encoded command which works but I find I can’t pass the hostname/ip parameter in at the end of the script with -args %1. I get ‘Cannot process command because a command is already specified with -Command or -EncodedCommand’
Anyone know if it’s possible to do what I’m trying to do?
Hey, Pete,
So you don’t know the parameters beforehand and want to dynamically pass them from bat file args to embedded powershell script?
I don’t think this is possible with EncodedCommand… There could be a few workarounds:
A. If the PowerShell script is very small and not confidential, you could probably just use PowerShell code as is (in -Command argument) and just escape various special characters.
B. You could pass the parameters via some other place – for example a text file you create in %temp% from bat and read from encoded PowerShell.
Dmitry
Hi Dimitry,
I’d like to dynamically pass the parameters as this is intended to be a quick cmd line tool to find out the owner/user of a pc/ip address. It was a dos one liner and then it became PS for the ease of allowing it to work with IPs as well, I grabbed some of the lines from your related script.
The script is not that complex or confidential so i’ve had a go at A. already but didn’t manage it, the encoded command was much easier. I need to have another go at this.
B. sounds interesting, I’ll try this if no joy with A.
Thanks
$name = $args
If ($args -match “\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}”)
{$name = ([system.net.dns]::gethostbyaddress($args)).HostName
$name = $name.replace(“.mydomain.name”,””)
}
If (!$args){
write-host “Usage: cwho2 computername/ip address”
Exit
}
dsquery computer -name $name| dsget computer -q -dn -loc -desc -L
Pete,
I will blog about B next week (Tuesday) – just got it working on my machine. 🙂 I’ll add a link here as well.
Dmitry
Dmitry, thanks very much for the write-up. Regarding non-zero exit codes, are you aware of any mechanism to actually treat them as errors? Or any other way for a script to signal an error condition to the task scheduler. Currently, if a script exits with an exit code != 1, this is noted in the “Last Run Result” (as described in your post), but the script invocation itself is logged as a successful action in the task history.
A plain old error would be closer to what I’d love to see. Or is my understanding wrong and the W2K8 semantics of a successful action can be boiled down to “well, we started something and it executed (regardless of its exit code)”?
^^ Should have been “with an exit code != 0” above.
I have a powershell script file called “MyScript.ps1” with the following command:
Set-DPMGlobalProperty -DpmServer “SYSCDPM01” -OptimizeTapeUsage $false
When I try to run it in
powershell -command “& ‘MyScript.ps1’ ”
I got the following error:
The term ‘Set-DPMGlobalProperty’ is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
At C:\BackupToTapeLibrary.ps1:1 char:22
+ Set-DPMGlobalProperty <<<< -DpmServer "SYSCDPM01" -OptimizeTapeUsage $false
How do I resolve it ?
HI Wyatt,
I think if you read point “1. Getting your script ready” at the top of this blog post, you will probably find the answer you are looking for.
Thanks for the help
@Sebastian Kayser:
I think there is no clean and easy solution to your problem. I had the same requirement that when I exit the script with an error code that the task scheduler does a certain amount of retries as configured. But the task scheduler doesn’t care.
Maybe the task scheduler doesn’t bother because the powershell.exe is executed and closed successfully. Hence, the task scheduler neglects the outcome of the script, although it is mentioned in the last execution result.
Then I tried whether the retry mechanism works in other situations. For this I scheduled a task which sends an email. But in the configuration I added a smtp server which doesn’t exist in order to enforce that the task action fails. Once again I get the same result the last execution result is logged but the task is not retried.
So I figure, either I am missing something in the logic of the retry mechanism or the retry mechanism doesn’t work at all.
Hey everyone,
I’m running this:
d:\SQLScripts>powershell.exe -Command “& D:\SQLScripts\GenerateScript.ps1 -serverName SERVER1 -dbname DB1 -scriptpath D:\SQLScripts\”
GenerateScript.ps1 parameters = serverName, dbname, scriptpath
And getting this error:
The string starting:
At line:1 char:103
+ & D:\SQLScripts\GenerateScript.ps1 -serverName SERVER1 -dbname DB1 -scriptpath D:\SQLScripts <<<< "
is missing the terminator: ".
At line:1 char:104
+ & D:\SQLScripts\GenerateScript.ps1 -serverName SERVER1 -dbname DB1 -scriptpath D:\SQLScripts" <<<<
+ CategoryInfo : ParserError: (:String) [], ParentContainsErrorRe
cordException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
I have tried a ton of different combinations of using single quotes and double quotes, not putting in the parameters identifiers (i.e. – -serverName) and keep getting the same "is missing the terminator" error.
Any thoughts on how to fix this or what I'm missing?
Thanks in advance!
Wade
This is probably because PowerShell parser is misinterpreting symbols in D:\SQLScripts\. Try putting the filename in quotes as well…
Does the same thing work from a cmd file?
I had tried it that way too but I may have messed up the syntax a bit too. This was pretty much one of my first attempts at running a PS script. Normally a DBA, but had to step out of my element a bit today.
🙂
Well I finally figured it out with the right combination. Ugh, that was painful. This is what ended up working:
powershell.exe -Command “”
Whoops…here it is:
powershell.exe -Command “”
OK 1 more time….man it’s been a long day already.
d:\SQLScripts>powershell.exe -Command “”
🙂
sorry, not sure why it’s not posting properly. I basically took out -serverName, -dbname and -scriptpath. Then I put everything after the “&” and before the last double quotes within these brackets {}
d:\SQLScripts>powershell.exe -Command “”
Hello All,
I’m Suresh please help me on this…! As we have to run URT script on all servers which are part of our domain every month and it will generate .Mef3 files on the located server so here i want create schedule the task or script so please help me on this so that i can save my lot of time,
-noninteractive: this can be extremely useful in the context of a scheduled task. There are cmdlets that prompt for information (like passwords) and by default powershell will prompt you for a missing script parameter. If PS blocks on input it will hang the task.
Using this flag causes an exception to be thrown instead of issuing a prompt.
I’d recommend this flag on any scheduled task.
Thanks Dimitri. This was very helpful. I found I was getting a 0x1 result on my tasks due to UAC on the local machine. I had to browse to the folder where I was running the script and access the files by bypassing the “Click here to gain access to these files” administrative rights prompt. Then my script ran properly. Hope that helps someone else.
Thanks Matt! Yes, having to unblock script files you get from the internet is indeed quite often the gotcha that people get. Thanks for adding this comment!
My shell is working but to schedule it is the problem
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -psconsolefile “C:\Program Files\Quest Software\Management Shell for AD\ConsoleSettings.psc1” -noexist -command “. ‘C:\Program Files\Quest Software\Management Shell for AD\qsft.ps1′” D:\ADReport\aduserreportsou.ps1
Get-qaduser –includedproperties DisplayName,Lastlogon –searchroot ‘OU=Employees,OU=Resources,DC=MC,DC=xyzconstruction,DC=co,DC=uk’ | select DisplayName,Lastlogon,Department | export-csv d:\ADReport\exportver2.csv
I go my solution and it worked successfull never give up.
1.Set-Executionpolicy Unrestricted
2.New-Item –path $profile –type file –force
Then edit the …..My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 and insert the following lines….
add-pssnapin quest.activeroles.admanagement
import-module ActiveDirectory
The schedule task with windows 2008R successfull
I had big problems running my scripts. It worked in the Exchange Console, and it worked from DOS prompt using powershell -file script.ps1 but not as a scheduled task. I’ve tried the -command and the -noninteractive parameters and I even tried using Start-Transcript in the script. Nothing worked. So I tried fiddling with the path of Powershell.exe and the path my of script etc. Nothing happend – no errors, nothing to debug.
In the end I found out that checking the “Run with highest privileges” solved my problem. Just wanted to share.
Tnx for sharing your guide and thanks for the ppl who commented on it.
Soon it will be a small or no problems
from powershell command
1.Set-Executionpolicy Unrestricted
2.New-Item –path $profile –type file –force
Then edit the …..My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 and insert the following lines….
add-pssnapin quest.activeroles.admanagement
import-module ActiveDirectory
Hi,
I run this in EMS:
Remove-Message -Server xxxxxx -Filter {MessageSourceName -eq “DSN”} -WithNDR $false.
It works.
Not If I try to schedule it or run it through the original script. The command expects an interactive response (parameter is a = yes).
Help me please!
Thank you.
Flaviano
Ok, solved:
First execute:
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010
then:
Remove-Message -Server xxxxxx -Filter {MessageSourceName -eq “DSN”} -WithNDR $false -Confirm:$False
I need help with PowerShell I would like to read a txt file which has a list of computers one per line then search a different txt file for each name and have it write to a file.
So Machine.txt has a list of computer name and master.txt has a list of computer names and serial number seperated by a : my goal is to run some kind of a powerShell to read machine.txt and search for each machine in master.txt and write the machine name and corresponding serial number to a txt file
Can anyone help please
Howdy! I simply would like to give an enormous thumbs up for the
great data you may have here on this post.
I can be coming back to your weblog for extra soon.
What’s Taking place i’m new to this, I stumbled upon this I’ve found It absolutely useful and it has helped me out loads. I’m hoping to
contribute & help different users like its aided me.
Good job.
Fantastic, saved my time and hair which I was about to tear off my head 😀 Brgs
What if I need to execute a few powershell script files or execute a few powershell commands after loading the Snap-in ? Note those powershell script files or powershell commands rely on the snap-in to execute properly.
Good article Dimitry. I hadn’t realized I should run my script at the cmd line to see possible errors that could be holding it up in the Task Manager. Running my script at the cmd line, in my script I tried add-PSSnapin Quest.activeroles.admanagement, and got the error add-PSSnapin : No snap-ins have been registered for Windows PowerShell version 3. also, I tried Set-ExecutionPokicy in my script and it says Access to the registry key ‘HKEY_LOCAL_machine\Software\Microsfot\PowerShell\1\ShellIds\Microsoft.PowerShell is denied. It seemed to work to do -ExecutionPolicy RemoteSigned at the command line. Now it’s saying “Access to S:\file.vhd is denied”. This was passed in as an argument. Any ideas? It looks like the powershell script can’t see a location on my physical server. I’m logged in through AD using my Administrators account.
any idea how to do a custom retry wrapper in the task scheduler for a powershell script- in case script failed I want it to retry, I gave it exit code that depends on the script itself – now I need somehow to make a catch try or something to restart the script in the task until exit code is 0
Same problem spent over an hour trying every suggestion on the Internet. No luck. Why is scheduling powershell sooo difficult!!!???
to Marcus Lewis: holy crap! this actually works! I have been searching everywhere for how to execute powershell through Task Scheduler without it flashing up momentarily. This did it !!
Just to clarify for others, I clicked on the button “Change User or Group” under the General tab in the Task Properties (edit task window).
Then just simply typed in “System” (without the inverted commas of course), and either click “Check Names” button or “OK”.
It will change the user account displayed at the left then to “NT AUTHORITY\SYSTEM” (although when you go in again after this it only displays “SYSTEM”)…
What it will also do is change the “Run only when user is logged on” to “Run whether user is logged on or not” and grey it out.
Worked for me! =)