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.

24 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 🙂

    1. Hi Luiz,

      Yes. This should work without issues with Apache as well. Not sure how forcing it to use a specific service account as I never used it.

  3. Thank you for the article, very useful

    I managed to run a basic powershell script from PHP using the example provided.

    Now I’m trying to post variable parameters to PHP URL and pass them to powershell script.
    I tested the script using the parameters as per below from another PS script and it works fine:
    C:\Users\obalanuta\Desktop\FileMove.ps1 -File File3

    For example I want to generate URL which will contain parameter “File” and pass it to the PowerShell script:
    http://localhost/testphp..php?File=File2

    Here is the php code I used but it won;t do anything:

    Any ideas as what I’m doing wrong?

    1. Hi Oleg, it looks like you haven’t pasted your PHP script. Can you send it again? Perhaps via https://pastebin.com/ if it’s too long?
      Note that “http://localhost/testphp.php?File=File2” is a GET method and not a POST. If GET is what you want, your PHP will look more or less like this:

      #Check if the GET Variable exists and if it does, trigger the script
      If (isset($_GET["File"])) {
      $Result = Shell_Exec ('powershell.exe -executionpolicy bypass -NoProfile -Command ".\FileMove.ps1 '.$_GET["File"].'"');
      }

      Make sure the account running the web worker has rights to trigger the script and rights to access/execute the script.
      Cheers

  4. When I tried to run the first script I got the following error message:
    “Fatal error: Maximum execution time of 30 seconds exceeded in C:\MBP\www\mbp\inc\mbp-move-window.php on line 40”

    It turned out that I had too many processes running and, when converted to HTML, the output was huge. I altered the above exampled with much better results:
    echo “

    ”;

    Note that I also added the parameter “-InputFormat none”! Without it, I would not get any error but the script would never finish running!

    1. Thanks for your input Elias. Where are you running your web server from? Looks like you either have an underspec’ed machine or a ton of processes 🙂 Get-Process should take just a few seconds.

  5. Hi,

    I hoped this article would be the solution to my problem. Unfortunately, it seems like it’s not, but probably I’m missing something.

    At the end of all configuring, I get the PHP-page to do simple commands without powershell, like “dir” and “whoami”. But that I could already do using the DefaultAppPool.

    It just fails when I’m trying to do a powershell-command like in your example.

    The page hangs for about 30 seconds and then times out. It just hangs and then returns error 500 with some suggestions:

    IIS received the request; however, an internal error occurred during the processing of the request. The root cause of this error depends on which module handles the request and what was happening in the worker process when this error occurred.
    IIS was not able to access the web.config file for the Web site or application. This can occur if the NTFS permissions are set incorrectly.
    IIS was not able to process configuration for the Web site or application.
    The authenticated user does not have permission to use this DLL.
    The request is mapped to a managed handler but the .NET Extensibility Feature is not installed.

    These powershell commands work fine when I start it from the cmd prompt, obviously.

    I also had some issues setting up the application pool and pool user. I created user phppool and made it an administrator. I created a pool and let the phppool user be the identity of that pool. That didn’t get me anywhere. Even the basic dir and whoami now failed. At the end I changed the anonymous authentication credentials of the web-application and set it to “Application pool identity” instead of the default IUSR. So now it works like the DefaultAppPool, but still no luck in executing other things than simple ‘dos’ commands, let alone powershell commands.

    I should mention this is on Vista. I want to purchase a Win10 machine for this project, but I must be sure I can get it to work before I buy something.

    At the end, I just need the PHP page to read/write a serial com port. The concept works in PowerShell, now I need to integrate that into PHP. I’m on this for a couple of weeks now and I think it’s frustrating that it’s so hard to do.

    Thanks for any suggestions.

    1. Hi,

      I tried sending you an email, not sure you got it, then I saw this.
      First of all, yep, this definitely works 🙂 I actively use this in multiple web applications. If you see the email, reach out to me there and drop me the configs/screenshots. It looks like IIS isn’t configured correctly.
      I’m not too sure about Vista though, so an idea would be spinning a test VM running Windows 10 to make sure it’s not an issue with Vista+IIS.

      Cheers

      1. For whomever may be looking at this comment. This was resolved by using < NUL at the end of the command: ..-Command “ipconfig” < NUL');'

  6. Line # on script needs edit

    from
    $MyHTMLCode = $Processes | ConvertToHTML
    to
    $MyHTMLCode = $Processes | ConvertTo-HTML

    (maybe you wanted us to catch? didn’t read well enough)

Leave a Reply

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