Posts Tagged ‘batch file’

Provide A Batch File To Run Your PowerShell Script From; Your Users Will Love You For It

November 16th, 2013 106 comments

Aside – This post has received many tangential questions in the comments. Your best bet at getting an answer to those questions is to check Stack Overflow and/or post your question there.

A while ago in one of my older posts I included a little gem that I think deserves it’s own dedicated post; calling PowerShell scripts from a batch file.

Why call my PowerShell script from a batch file?

When I am writing a script for other people to use (in my organization, or for the general public) or even for myself sometimes, I will often include a simple batch file (i.e. *.bat or *.cmd file) that just simply calls my PowerShell script and then exits.  I do this because even though PowerShell is awesome, not everybody knows what it is or how to use it; non-technical folks obviously, but even many of the technical folks in our organization have never used PowerShell.

Let’s list the problems with sending somebody the PowerShell script alone; The first two points below are hurdles that every user stumbles over the first time they encounter PowerShell (they are there for security purposes):

  1. When you double-click a PowerShell script (*.ps1 file) the default action is often to open it up in an editor, not to run it (you can change this for your PC).
  2. When you do figure out you need to right-click the .ps1 file and choose Open With –> Windows PowerShell to run the script, it will fail with a warning saying that the execution policy is currently configured to not allow scripts to be ran.
  3. My script may require admin privileges in order to run correctly, and it can be tricky to run a PowerShell script as admin without going into a PowerShell console and running the script from there, which a lot of people won’t know how to do.
  4. A potential problem that could affect PowerShell Pros is that it’s possible for them to have variables or other settings set in their PowerShell profile that could cause my script to not perform correctly; this is pretty unlikely, but still a possibility.
      So imagine you’ve written a PowerShell script that you want your grandma to run (or an HR employee, or an executive, or your teenage daughter, etc.). Do you think they’re going to be able to do it?  Maybe, maybe not.

You should be kind to your users and provide a batch file to call your PowerShell script.

The beauty of batch file scripts is that by default the script is ran when it is double-clicked (solves problem #1), and all of the other problems can be overcome by using a few arguments in our batch file.

Ok, I see your point. So how do I call my PowerShell script from a batch file?

First, the code I provide assumes that the batch file and PowerShell script are in the same directory.  So if you have a PowerShell script called “MyPowerShellScript.ps1” and a batch file called “RunMyPowerShellScript.cmd”, this is what the batch file would contain:

SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%MyPowerShellScript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'";

Line 1 just prevents the contents of the batch file from being printed to the command prompt (so it’s optional).  Line 2 gets the directory that the batch file is in.  Line 3 just appends the PowerShell script filename to the script directory to get the full path to the PowerShell script file, so this is the only line you would need to modify; replace MyPowerShellScript.ps1 with your PowerShell script’s filename.  The 4th line is the one that actually calls the PowerShell script and contains the magic.

The –NoProfile switch solves problem #4 above, and the –ExecutionPolicy Bypass argument solves problem #2.  But that still leaves problem #3 above, right?

Call your PowerShell script from a batch file with Administrative permissions (i.e. Run As Admin)

If your PowerShell script needs to be run as an admin for whatever reason, the 4th line of the batch file will need to change a bit:

SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%MyPowerShellScript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%PowerShellScriptPath%""' -Verb RunAs}";

We can’t call the PowerShell script as admin from the command prompt, but we can from PowerShell; so we essentially start a new PowerShell session, and then have that session call the PowerShell script using the –Verb RunAs argument to specify that the script should be run as an administrator.

And voila, that’s it.  Now all anybody has to do to run your PowerShell script is double-click the batch file; something that even your grandma can do (well, hopefully).  So will your users really love you for this; well, no.  Instead they just won’t be cursing you for sending them a script that they can’t figure out how to run.  It’s one of those things that nobody notices until it doesn’t work.

So take the extra 10 seconds to create a batch file and copy/paste the above text into it; it’ll save you time in the long run when you don’t have to repeat to all your users the specific instructions they need to follow to run your PowerShell script.

I typically use this trick for myself too when my script requires admin rights, as it just makes running the script faster and easier.


One more tidbit that I often include at the end of my PowerShell scripts is the following code:

# If running in the console, wait for input before closing.
if ($Host.Name -eq "ConsoleHost")
	Write-Host "Press any key to continue..."
	$Host.UI.RawUI.FlushInputBuffer()	# Make sure buffered input doesn't "press a key" and skip the ReadKey().
	$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null

This will prompt the user for keyboard input before closing the PowerShell console window.  This is useful because it allows users to read any errors that your PowerShell script may have thrown before the window closes, or even just so they can see the “Everything completed successfully” message that your script spits out so they know that it ran correctly.  Related side note: you can change your PC to always leave the PowerShell console window open after running a script, if that is your preference.

I hope you find this useful.  Feel free to leave comments.

Happy coding!


Several people have left comments asking how to pass parameters into the PowerShell script from the batch file.

Here is how to pass in ordered parameters:

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%' 'First Param Value' 'Second Param Value'";

And here is how to pass in named parameters:

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%' -Param1Name 'Param 1 Value' -Param2Name 'Param 2 Value'"

And if you are running the admin version of the script, here is how to pass in ordered parameters:

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File """"%PowerShellScriptPath%"""" """"First Param Value"""" """"Second Param Value"""" ' -Verb RunAs}"
And here is how to pass in named parameters:
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File """"%PowerShellScriptPath%"""" -Param1Name """"Param 1 Value"""" -Param2Name """"Param 2 value"""" ' -Verb RunAs}";
And yes, the PowerShell script name and parameters need to be wrapped in 4 double quotes in order to properly handle paths/values with spaces.

Parallel MSBuild FTW – Build faster in parallel

March 31st, 2012 1 comment

Hey everyone, I just discovered this great post yesterday that shows how to have msbuild build projects in parallel Smile

Basically all you need to do is pass the switches “/m:[NumOfCPUsToUse] /p:BuildInParallel=true” into MSBuild.

Example to use 4 cores/processes (If you just pass in “/m” it will use all CPU cores):

MSBuild /m:4 /p:BuildInParallel=true "C:devClient.sln"

Obviously this trick will only be useful on PCs with multi-core CPUs (which we should all have by now) and solutions with multiple projects; So there’s no point using it for solutions that only contain one project.  Also, testing shows that using multiple processes does not speed up Team Foundation Database deployments either in case you’re curious Winking smile

Also, I found that if I didn’t explicitly use “/p:BuildInParallel=true” I would get many build errors (even though the MSDN documentation says that it is true by default).

The poster boasts compile time improvements up to 59%, but the performance boost you see will vary depending on the solution and its project dependencies.  I tested with building a solution at my office, and here are my results (runs are in seconds):

# of Processes 1st Run 2nd Run 3rd Run Avg Performance
1 192 195 200 195.67 100%
2 155 156 156 155.67 79.56%
4 146 149 146 147.00 75.13%
8 136 136 138 136.67 69.85%


So I updated all of our build scripts to build using 2 cores (~20% speed boost), since that gives us the biggest bang for our buck on our solution without bogging down a machine, and developers may sometimes compile more than 1 solution at a time.  I’ve put the any-PC-safe batch script code at the bottom of this post.

The poster also has a follow-up post showing how to add a button and keyboard shortcut to the Visual Studio IDE to have VS build in parallel as well (so you don’t have to use a build script); if you do this make sure you use the .Net 4.0 MSBuild, not the 3.5 one that he shows in the screenshot.  While this did work for me, I found it left an MSBuild.exe process always hanging around afterwards for some reason, so watch out (batch file doesn’t have this problem though).  Also, you do get build output, but it may not be the same that you’re used to, and it doesn’t say “Build succeeded” in the status bar when completed, so I chose to not make this my default Visual Studio build option, but you may still want to.

Happy building!

:: Calculate how many Processes to use to do the build.
SET NumberOfProcessesToUseForBuild=1 
SET BuildInParallel=false
                SET NumberOfProcessesToUseForBuild=2
                SET BuildInParallel=true
MSBuild /maxcpucount:%NumberOfProcessesToUseForBuild% /p:BuildInParallel=%BuildInParallel% "C:\dev\Client.sln"