Run a Powershell script from PHP

Running a Powershell script from PHP is easier than I expected. I was pretty new to this as well, but after a while I manage to build some pretty nice automatized tasks that help a lot with small processes.

Of course, if you’re using Windows Powershell, you’ll need to run these script from a Windows Server/Client.

If you’re in a Windows environment, the best way to go is installing PHP in IIS. I recommend using the latest PHP version (at the time of writing, we’re at 7.2), you can grab it from https://php.iis.net, or you can launch the Web Platform Installer if you’ve already got it in IIS.

Possibilities

Many! You really have a huge playground here to develop whatever you need. Here’s a few examples:

  • Automate the creation of an AD account and allow access to the web end to just the Help Desk team.
    • What does this mean? You’ll no longer need to delegate permissions to the entire Help Desk team, you’ll just need to delegate permissions to the service account running the Application Pool in IIS. Also, because nobody else has permissions, you can choose the way you want this AD Account to be created (base OU, syntax, password length, settings etc).
  • Allow users to change a specific setting in their AD Account.
    • Imagine a large organization, you may want to delegate as many tasks as possible. For example, say we’re ok to trust the users to change their own Phone Numbers in AD. You can build a script that will allow to do that, at your own conditions and expose a small web interface to allow the user to see the current phone number and change it.
  • Allow the Help Desk and Desktop Teams to view a share’s NTFS permissions.
    • Once again, no need to provide access to the share, just the service account will need access. You’ll build a script that will grab the ACL from a share and return just that, based on the User’s input.

These are very basic examples of course. For instance, I’ve also built a tool that will allow Group’s managers to add/remove users to these groups. Super handy.

IIS Setup

The IIS setup is very simple. Once you’ve got PHP installed go ahead and follow these quick steps to create a new Application Pool and set it to run with a specific service account. It’s best when this service account is set as the local Admin of the server/client where it’s running from (unless you don’t need to perform any admin action).

Ready to execute the Powershell code

Let’s start with a very basic example, execute a specific powershell command. Start with creating a .php file and add this bit of code:

Now, when loading this .php page, you’ll get a list of processes running on your web server. The result should be view-friendly as we’re converting the output with ConvertTo-Html. I like to use -NoProfile to avoid loading any pre-set profile scripts that I may have set and also I prefer to call it with -executionpolicy bypass to avoid issues with the execution policy.

Next, you have a powershell script, called MyPSscript.ps1 in the same directory as the .php file and it contains:

This is how the PHP file now looks

Let’s review the code, which is called when the PHP page is launched:

  • Try to get all processes in $Processes
    • Convert the processes in HTML so that it’s human friendly when called in the browser
    • Return $MyHTMLCode to the PHP script
  • If there was an error in performing any of the actions above, return plain text (and as you can see, I added some HTML in there manually) which says that there was an error and also reports it ($_.Exception.Message).

I did this example to show you that you can really return whatever you like to the PHP page. You may even put the Shell_Exec() in a variable, and call it at a later stage:

Remember that everything within Shell_Exec() is simple command prompt code. So if you have a .ps1 script that accepts arguments, you can call them like this:

Sanitize all user input before passing them to Powershell

This is super important. The scripts above are “safe” because you’re executing some static content. But say you’re passing something to the powershell script, something the user has inputted:

So if the user manually visits in http://myserver/mytest.php?myvar=ThisIsMyVariable, ThisIsMyVariable will now be stored in $UserVariable. If you were to pass $UserVariable without sanitizing the input, you’ll be running a huge risk. The user may actually type malicious content that will execute the script with the service account and if that account has enough rights, it can cause some serious damage.

A simple way to sanitize the string is using escapeshellarg:

I personally use also ad-hoc checks depending on the usage I need. For example, if I know the string that I need to pass to my scripts must not have dashes, semi-columns etc, I make sure I check for these chars and if found, I return an error.

13 thoughts on “Run a Powershell script from PHP

  1. So I am running the echo Shell_Exec (‘powershell.exe -executionpolicy bypass -NoProfile -File “.\MyPSscript.ps1″‘); line and it pops up that windows powershell copyright notice to show it ran, but the .ps1 never runs, Would you know why?

    1. It depends on what MyPSscript.ps1 is doing. Start with something simpler, like running a single command line, even an ipconfig, and see if you can get that displayed in your PHP page. Then you can move to a one liner that saves the ipconfig somewhere as a text file to make sure it works and finally with a simple .ps1 that may return Get-Process for example to the PHP page calling it.

      1. Simon,
        I tried to run Get-NetIPConfiguration | convertto-html inside a script named, psscript.ps1 in the same wwwroot directory as the .php file with the php script and it doesn’t run, but when I run a basic command in the .php script such as Get-Process | ConvertTo-html as written above it works fine. Is there something in IIS that needs to be turned on to run scripts?

      2. So my mistake on the last update, it does work when I run a simple script with a one line command in it, but I am trying to run a script that will create a Virtual machine using vmware PowerCLI. It works from the server end when I run the script from normal Powershell, but when I put that file in the .php script nothing happens.

        1. Hi Rob, I am thinking of permission issues.. Are you running Connect-ViServer and if so, are you passing the right credentials to it?
          I am thinking, try to add Start-Transcript (and make sure you give a path which is writable by the account running the script) and then Stop-Transcript at the end of it, so that you can actually see if anything is being executed at all.

          1. ok. I ran that from the server and got the correct response, but when I run the .php page (From the server and from a client) I get a location to where it has saved the transcript, but it’s not there. That tells me it is running the script, but not saving the file. Any insights or locations you can point me towards?

  2. Start-Transcript allows you to choose where to save the file:
    Start-Transcript -Path c:\inetpub\mysite\transcript.txt

    1. weird. Seems to only make a transcript file when I run the Powershell script from Powershell on the server, not from the browser via the .php.

        1. I changed the app pool user ID from a local account to a domain account that had some admin rights. The transcript ran from the web and did an output to a .txt onto the server for me to read, but now it’s saying, “Connect-viserver : The term ‘Connect-viserver’ is not recognized as the name of a cmdlet, function, script file, or
          operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. So that tells me I’m half-way there, and now have to figure out why it won’t see the VMWare PowerCLI part of the puzzle via the web.

          1. Good! You just gotta import the PowerCLI snapin/module now, which wasn’t loaded automatically. I’m on my mobile and don’t recall the command, a quick google search will show you how 🙂

Leave a Reply

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