Packaging a Custom PHP Installation for Windows Azure

One feature of the scaffolds that are in the Windows Azure SDK for PHP is that all rely on the Web Platform Installer to install PHP when a project is deployed. This is great until I want my application deployed with my locally customized installation of PHP. Not only might I have custom settings, but I might have custom libraries that I want to include (like some PEAR modules or any of the many PHP frameworks out there). In this tutorial, I’ll walk you through the steps for deploying a PHP application bundled with a custom installation of PHP. This tutorial does not rely on the Windows Azure SDK for PHP, but you will need…

Ultimately, I’d like to make this process (below) easier (by possibly turning this into a scaffold to be included in the Windows Azure SDK for PHP?), so please provide feedback if you try this out…

1. Customize your PHP installation. Configure any settings and external libraries you want for your application.

Edit: As pointed out in the comments, if all you want/need to do is customize PHP settings or include some extensions, you can do this using the default scaffolder. Just open the /php/php.ini file that is created by the default scaffolder, add any extensions or directives you want for your PHP version, and deploy your application. The additions to the php.ini file will be appended to the php.ini file that is used for your PHP installation running in Azure. However, if your customizations are more extensive than this, read on...

2. Create a project directory. You’ll need to create a project directory for your application and the necessary Azure configuration files. Ultimately, your project directory should look something like this (we’ll fill in some of the missing files in the steps that follow):

-YourProjectDirectory
-YourApplicationRootDirectory
-bin
-configureIIS.cmd
-PHP
-(application files)
-(any external libraries)
-web.config
-ServiceDefinition.csdef

A few things to note about the structure above:

  • You need to copy your custom PHP installation to the bin directory.
    • Make sure that all paths in your php.ini are relative (e.g. extension_dir=”.\ext”)
  • Any external libraries need to be in your application root directory.
    • Technically, this isn’t true. You could use a relative path for your include_path configuration setting (relative to your application root) and put this directory elsewhere in your project directory.
  • Maybe this goes without saying, but be sure to turn off any debug settings (like display_errors) before pushing this to production in Azure.

3. Add a startup script for configuring IIS. IIS in a Web role is not configured to handle PHP requests by default. So, we need a script to run when an instance is brought on line to configure IIS. (We’ll set this script to run on start up in the next step.) Create a file called configureIIS.cmd, add the content below, and save it in the bin directory:

 @ECHO ON
  
 SET PHP_FULL_PATH=%~dp0php\php-cgi.exe
 SET NEW_PATH=%PATH%;%RoleRoot%\base\x86
  
 %WINDIR%\system32\inetsrv\appcmd.exe set config -section:system.webServer/fastCgi /+"[fullPath='%PHP_FULL_PATH%',maxInstances='12',idleTimeout='60000',activityTimeout='3600',requestTimeout='60000',instanceMaxRequests='10000',protocol='NamedPipe',flushNamedPipe='False']" /commit:apphost
 %WINDIR%\system32\inetsrv\appcmd.exe set config -section:system.webServer/fastCgi /+"[fullPath='%PHP_FULL_PATH%'].environmentVariables.[name='PATH',value='%NEW_PATH%']" /commit:apphost
 %WINDIR%\system32\inetsrv\appcmd.exe set config -section:system.webServer/fastCgi /+"[fullPath='%PHP_FULL_PATH%'].environmentVariables.[name='PHP_FCGI_MAX_REQUESTS',value='10000']" /commit:apphost
 %WINDIR%\system32\inetsrv\appcmd.exe set config -section:system.webServer/handlers /+"[name='PHP',path='*.php',verb='GET,HEAD,POST',modules='FastCgiModule',scriptProcessor='%PHP_FULL_PATH%',resourceType='Either',requireAccess='Script']" /commit:apphost
 %WINDIR%\system32\inetsrv\appcmd.exe set config -section:system.webServer/fastCgi /"[fullPath='%PHP_FULL_PATH%'].queueLength:50000"

 

4. Add a service definition file (ServiceDefinition.csdef). Every Azure application must have a service definition file. The important part of this one is that we set the script above to run on start up whenever an instance is provisioned:

 <?xml version="1.0" encoding="utf-8"?>
 <ServiceDefinition name="YourProjectDirectory" xmlns="https://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
     <WebRole name="YourApplicationRootDirectory" vmsize="ExtraSmall" enableNativeCodeExecution="true">
         <Sites>
             <Site name="YourPHPSite" physicalDirectory="./YourApplicationRootDirectory">
                 <Bindings>
                     <Binding name="HttpEndpoint1" endpointName="defaultHttpEndpoint" />
                 </Bindings>
             </Site>
         </Sites>
         <Startup>
             <Task commandLine="configureIIS.cmd" executionContext="elevated" taskType="simple" />
         </Startup>
         <InputEndpoints>
             <InputEndpoint name="defaultHttpEndpoint" protocol="http" port="80" />
         </InputEndpoints>
     </WebRole>
 </ServiceDefinition>

Note that you will need to change the names of YourProjectDirectory and YourApplicationRootDirectory depending on what names you used in your project structure from step 1. You can also configure the VM size (which is set to ExtraSmall in the file above). For more information, see Windows Azure Service Definition Schema.

5. Generate a service configuration file (ServiceConfiguration.cscfg). Every Azure application must also have a service configuration file, which you can generate using the Windows Azure SDK. Open a Windows Azure SDK command prompt, navigate to your project directory, and execute this command:

 cspack ServiceDefinition.csdef /generateConfigurationFile:ServiceConfiguration.cscfg /copyOnly

This will generate a ServiceConfiguration.cscfg file and a ServiceDefinition.csx directory in your project directory.

6. Run your application in the Compute Emulator. If you want to run your application in the Compute Emulator (for testing purposes), execute this command:

 csrun ServiceDefinition.csx ServiceConfiguration.cscfg /launchbrowser

One thing to note about doing this: The configureIIS.cmd script will be executed on your local machine (setting your PHP handler to point to the PHP installation that is part of your Azure project). You’ll need to change this later.

7. Create a package for deployment to Azure. Now you can create the package file (.cspkg) that you need to upload to Windows Azure with this command:

 cspack ServiceDefinition.csx ServiceConfiguration.cscfg

8. Deploy your application. Finally, you can deploy your application. This tutorial will walk you through the steps: https://azurephp.interoperabilitybridges.com/articles/deploying-your-first-php-application-to-windows-azure#new_deploy.

Again, I’d be very interested to hear feedback from anyone who tries this. Like I mentioned earlier, I think turning this into a scaffold that is included in the Windows Azure SDK for PHP might be very useful.

Thanks.

-Brian

Share this on Twitter