Thursday, 19 March 2009

Quickly put PHP on Windows Azure without Visual Studio

The purpose of this post is to show you the command line options you have to first get PHP running locally on IIS7 with fastCGI and the to get it packaged and running in the Windows Azure local development fabric.

First, download the Windows Azure SDK and the latest version of PHP (or whatever version you wish).  I would recommend getting the .zip version and simply extracting to "C:\PHP" or something like that.  Now, configure your php.ini file according to best practices.

Next, create a new file called ServiceDefinition.csdef and copy the following into it:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="MyPHPApp" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="WebRole" enableNativeCodeExecution="true">
<InputEndpoints>
<!-- Must use port 80 for http and port 443 for https when running in the cloud -->
<InputEndpoint name="HttpIn" protocol="http" port="80" />
</InputEndpoints>
</WebRole>
</ServiceDefinition>

You will need this file in order to describe your application in the cloud.  This definition models what your application should look like in the cloud.  For this example, it is a simple web role listening to port 80 over HTTP.  Notice as well that we have the 'enableNativeCodeExecution' attribute set as well.  This is required in order to use fastCGI.

Then, create a .bat file for enabling fastCGI running locally.  I called mine PrepPHP.bat:

@echo off

set phpinstall=%1
set appname=%2
set phpappdir=%3

echo Removing existing virtual directory...
"%windir%\system32\inetsrv\appcmd.exe" delete app "Default Web Site/%appname%"

echo.
echo Creating new virtual directory...
"%windir%\system32\inetsrv\appcmd.exe" add app /site.name:"Default Web Site" /path:"/%appname%" /physicalPath:"%phpappdir%"

echo.
echo Updating applicationHost.config file with recommended settings...
"%windir%\system32\inetsrv\appcmd.exe" clear config -section:fastCGI
"%windir%\system32\inetsrv\appcmd.exe" set config -section:fastCgi /+"[fullPath='%phpinstall%\php-cgi.exe']

echo.
echo Setting PHP handler for application
"
%windir%\system32\inetsrv\appcmd.exe" clear config "Default Web Site/%appname%" -section:system.webServer/handlers
"
%windir%\system32\inetsrv\appcmd.exe" set config "Default Web Site/%appname%" -section:system.webServer/handlers /+[name='PHP_via_FastCGI',path='*.php',verb='*',modules='FastCgiModule',scriptProcessor='%phpinstall%\php-cgi.exe',resourceType='Unspecified']

echo.
echo Setting Default Document to index.php
"
%windir%\system32\inetsrv\appcmd.exe" clear config "Default Web Site/%appname%" -section:defaultDocument
"
%windir%\system32\inetsrv\appcmd.exe" set config "Default Web Site/%appname%" -section:defaultDocument /enabled:true /+files.[@start,value='index.php']

echo.
echo Done...

Create one more .bat file to enable PHP running in the local dev fabric:

@echo off

set phpinstall=%1
set appname=%2
set phpappdir=%3

echo.
echo Setting PHP handler for application
"%windir%\system32\inetsrv\appcmd.exe" set config "Default Web Site/%appname%" -section:system.webServer/handlers /[name='PHP_via_FastCGI'].scriptProcessor:%%RoleRoot%%\php\php-cgi.exe

echo.
echo Outputting the web.roleconfig file
del "%phpappdir%\web.roleconfig" /q

echo ^<;?xml version="1.0" encoding="utf-8" ?^> > "%phpappdir%\web.roleconfig"
echo ^<;configuration^> >> "%phpappdir%\web.roleconfig"
echo ^<;system.webServer^> >> "%phpappdir%\web.roleconfig"
echo ^<;fastCgi^> >> "%phpappdir%\web.roleconfig"
echo ^<;application fullPath="%%RoleRoot%%\php\php-cgi.exe" /^> >> "%phpappdir%\web.roleconfig"
echo ^<;/fastCgi^> >> "%phpappdir%\web.roleconfig"
echo ^<;/system.webServer^> >> "%phpappdir%\web.roleconfig"
echo ^<;/configuration^> >> "%phpappdir%\web.roleconfig"

echo Copying php assemblies and starting fabric

md %appname%_WebRole
md %appname%_WebRole\bin

robocopy %phpappdir% %appname%_WebRole\bin /E
robocopy %phpinstall% %appname%_WebRole\bin\php /E

"%programfiles%\windows azure sdk\v1.0\bin\cspack.exe" "%~dp0ServiceDefinition.csdef" /role:WebRole;"%~dp0%appname%_WebRole\bin" /copyOnly /generateConfigurationFile:"%~dp0ServiceDefinition.csx\ServiceConfig.cscfg"
"%programfiles%\windows azure sdk\v1.0\bin\csrun.exe" "%~dp0ServiceDefinition.csx" "%~dp0ServiceDefinition.csx\ServiceConfig.cscfg" /launchBrowser

echo.
echo Done...

Open a command prompt as an Administrator and make sure that the ServiceDefinition.csdef file you created is in the same directory as the .bat files you created.

From the command line, type:

PrepPHP.bat "path to php binaries directory" "myiis7appname" "path to my php app"

If I had installed PHP to "c:\php" and my PHP application was located at "c:\webroot\myphpapp", I would type:

prepphp.bat "c:\php" "myphpapp" "c:\webroot\myphpapp"

Now, I can launch IE and type in:  http://localhost/myphpapp and the index.php page will launch.

To get this running in Windows Azure local dev fabric, you would type:

prepforazure.bat "c:\php" "myphpapp" "c:\webroot\myphpapp"

The dev fabric will launch as well as IE and you will be looking at your PHP application running in the dev fabric.  If you wish to deploy to the cloud, simply change the command line call for cspack.exe to remove the 'copyOnly' option.  Next, comment out the csrun.exe and you have a package ready to upload to the cloud.  When you deploy at the portal, make sure you update the number of instances on the portal to at least 2 in order to get fault tolerance.