What's the best way to execute PowerShell scripts from Python What's the best way to execute PowerShell scripts from Python powershell powershell

What's the best way to execute PowerShell scripts from Python


Arguably the best approach is to use powershell.exe -Command rather than writing the PowerShell command to a file:

pscommand = 'Invoke-Command ...'process = subprocess.Popen(['powershell.exe', '-NoProfile', '-Command', '"&{' + pscommand + '}"'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

Make sure double quotes in the pscommand string are properly escaped.

Note that shell=True is required only in certain edge cases, and should not be used in your scenario. From the documentation:

On Windows with shell=True, the COMSPEC environment variable specifies the default shell. The only time you need to specify shell=True on Windows is when the command you wish to execute is built into the shell (e.g. dir or copy). You do not need shell=True to run a batch file or console-based executable.


After spending a fair amount of time on this.

I think that running powershell commands from python may not make sense to a lot of people, especially people who work exclusively in windows environments. There are numerous clear advantages to python over powershell however so the ability to do all your business logic in python and then selectively execute powershell on remote servers is truly a great thing.

I've now been through several improvements of my "winrmcntl" module which I can't share due to company policy unfortunately but here is my advice to anyone who would like to do something similar. The module should take as input an unmodified PS command or scriptblock as you'd run it if you were typing directly in PS on the destination box. A few tricks:

  • To avoid permission difficulties, ensure the user running your python script and hence the one running powershell.exe via process.Popen is the user that has the correct permissions on the windows box you're invoke-command is pointing at. We use an enterprise scheduler which has windows vms as agents on which the python code lives which takes care of that.
  • You will sometimes rarely but still get the odd esoteric exception from powershell land, if they're anything like the one in particular I saw the odd time, microsoft scratch their heads at a little and get you to do time consuming application stack tracing. This is not only time consuming but very difficult to get right because it's resource intensive and you don't know when the exception will next occur. In my opinion, it's much better and easier to parse the output of the exception and retry up to x number of times if a certain text appears in those exceptions. I keep a list of strings in my winrmcntl module which currently contains a single string.
  • If you want to not have to "massage" the powershell commands as they traverse the python -> windows -> powershell -> powershell stack to make them work as expected on destination boxes, the most consistent method I've found is to write your one liners and scriptblocks alike into a ps_buffer.ps1 file which you then feed to powershell on the source box so that every process.popen looks exactly the same but the content of ps_buffer.ps1 changes with each execution.

    powershell.exe ps_buffer.ps1

  • To keep your python code nice and clean, it's great having your list of powershell one liners in a json file or similar as well as pointers to scriptblocks you want to run saved into static files. You load up your json file as an ordered dict and cycle through issuing commands based on what you're doing.

  • Can't be overstated, as far as is possible try to be on the latest stable version of PS but more than that, it's imperative to be on the same version on client and server.

"scriptblock" and "server" are the values fed to this module or function

import subprocessfrom string import Templatescriptblock = 'Get-ChildItem' #or a PS scriptblock as elaborate as you need   server = 'serverx'psbufferfile = os.path.join(tempdir, 'pscmdbufferfile_{}.ps1'.format(server))fullshellcmd = 'powershell.exe {}'.format(psbufferfile)raw_pscommad = 'Invoke-Command -ComputerName $server -ScriptBlock {$scriptblock}'pscmd_template = Template(raw_pscommand)pscmd = pscmd_template.substitute(server=server, scriptblock=scriptblock)try:    with open(psbufferfile, 'w') as psbf:    psbf.writelines(pscmd)....try:    process = subprocess.Popen(fullshellcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)    output, error = process.communicate()....