Run a Powershell script from PHP

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:

<?PHP
  echo Shell_Exec ('powershell.exe -executionpolicy bypass -NoProfile -Command "Get-Process | ConvertTo-Html"');
?>

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:

Try  {
  #Store all processes in $MyProcesses
  $Processes = Get-Process

  #Convert $Processes in HTML
  $MyHTMLCode = $Processes | ConvertTo-HTML

  #Return the HTML code to the PHP Page
  Return $MyHTMLCode
}
Catch  {
  #Something went wrong
  Return "Oops: Something went wrong.<br />$($_.Exception.Message)<br />"
}

This is how the PHP file now looks

<?PHP
  echo Shell_Exec ('powershell.exe -executionpolicy bypass -NoProfile -File ".\MyPSscript.ps1"');
?>

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:

<?PHP
  $Result = Shell_Exec ('powershell.exe -executionpolicy bypass -NoProfile -File ".\MyPSscript.ps1"');
  
  #Do something

  echo $Result;
?>

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:

powershell.exe -executionpolicy bypass -NoProfile -File ".\MyPSscript.ps1 MyArgument1 MyArgument2"

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:

<?PHP

  $UserVariable = $_GET['myvar'];

?>

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:

<?PHP

  $UserVariable = escapeshellarg($_GET['myvar']);

?>

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.

IT Droplets

IT Droplets