Friday, February 26, 2016

Powershell Quick Script - Wrap any powershell script into a batch file

It's been a number of months since I've written anything here which is unfortunate since I've been writing lots of nifty things in powershell for work. So this should be the first of many scripts which need sharing.

I recently ran into an issue where I had to make a single file script which could easily be ran by a simple user who could just double-click on it and it would just work, regardless of which version of powershell they had or if their Execution Policy was set correctly. The solution I went for was the embed a powershell script inside of a batch file which is more universally accepted on legacy systems and by most windows admins.

There are several solutions I found which involve encoding the powershell script into a long base64 string and feeding it into powershell.exe, but this has a size limitation which larger scripts easily hit. Another solution I saw was to strip away special formatting, comments, certain characters, and then wrap it in curly brackets "{}" and again feed it to powershell.exe as a command. This too also suffers from the max length problem as well as requires special editing to make it work.

My solution is much simpler, suffers from no length constraints and really has one drawback which matters if you are watching the error output stream. The solution is very simple. Just add the following line before your powershell script:

goto ExecutePowershell
cls

Then add the following after your script:

exit
#end of powershell code - batch code now:
:ExecutePowershell
echo off
set filename=%temp%\Tempscript.ps1
copy /y %0 %filename%
echo NOTE - you will see error output in the error stream about 'goto' - this is expected and can be ignored.
cls
powershell -ExecutionPolicy unrestricted -file %filename% %*
set /a el=%errorlevel%
del %filename%
exit %el%

Finally, save your script as a .cmd or .bat file extension.  What this will do is cause your code to be executed as batch, which will then copy itself to the temp location and add the extension ps1 and then feed that into powershell.exe while bypassing the execution policy ofthe machine. Once the powershell code finishes, it will exit, returning back to the batch wrapper which will then clean up the temp ps1 file and return the error code from powershell exit. I highly suggest your powershell code has it's own built in exits. Most automation systems are specifically looking for exit codes and you want exit with 0 if it's successful or another number if it's not. If you do don't want the script to close the window when it's done, replace the last line "exit %el%" with "pause". Here's an example of the complete file: