Installing PHP on Windows Azure leveraging Full IIS Support: Part 2

Update: The Windows Azure PowerShell CmdLets have added the necessary bootstrap information in order to set up PHP on Windows Azure Cloud Services. Download the Windows Azure PowerShell CmdLets.

In the last post of this Series, Installing PHP on Windows Azure leveraging Full IIS Support: Part 1, we created a script to launch the Web Platform Installer Command-line tool in Windows Azure in a Command-line Script.

In this post we’ll be looking at creating the Service Definition and Service Configuration files which will describe what are deployment is to consist of to the Fabric Controller running in Windows Azure.

Creating a Windows Azure Service Definition

Unfortunately there isn’t a magical tool that will create a starter point for our Service Definition file, this is mostly due to the fact that Microsoft doesn’t know what to provide as a default. Windows Azure is all about Developer freedom, you create your application and let Microsoft worry about the infrastructure that it’s running on.

Luckily, Microsoft has documented the Service Definition (.csdef) file on MSDN, so we can use this documentation to guide us through the creation of our Service Definition. Let’s create a file called ‘ServiceDefinition.csdef’ outside of our Deployment folder. We’ll add the following content to the file, and I’ll explain a few of the key elements below.

Defining a Windows Azure Service

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="PHPonAzure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
	<WebRole name="Deployment" vmsize="ExtraSmall" enableNativeCodeExecution="true">
		<Startup>
			<Task commandLine="install-php.cmd" executionContext="elevated" taskType="background" />
		</Startup>
		<Endpoints>
			<InputEndpoint name="defaultHttpEndpoint" protocol="http" port="80"/>
		</Endpoints>
		<Imports>
			<Import moduleName="RemoteAccess"/>
			<Import moduleName="RemoteForwarder"/>
		</Imports>
		<Sites>
			<Site name="PHPApp" physicalDirectory=".\Deployment\Websites\MyPHPApp">
				<Bindings>
          				<Binding name="HttpEndpoint" endpointName="defaultHttpEndpoint" />
        				</Bindings>
      			</Site>
		</Sites>
	</WebRole>
</ServiceDefinition>

We will be using a Windows Azure WebRole to Host our application [remember WebRoles are IIS enabled], you’ll notice that our first element within our Service Definition is WebRole. Two Important pieces of the WebRole Element are the vmsize and enableNativeCodeExecution attributes. The VMSize Attribute hands off the VM Sizing requirements to the Fabric Controller so it can allocate our new WebRole. For those of you familiar with the .NET Stack the enabledNativeCodeExecution attribute will allow for FullTrust if set to true, or MediumTrust if set to false [For those of you that aren’t familiar, Here’s a Description of the Trust Levels in ASP.NET]. The PHP Modules for IIS need elevated privileges to run so we will need to set enableNativeCodeExecution to true.

In Part one of this series we created a command-line script that would initialize a PHP installation using WebPI. You’ll notice under the Startup Element, we’ve added our script to a list of Task Elements which defines the startup Tasks that are to be run on the Role. These scripts will run in the order stated with either limited [Standard User Access] or elevated [Administrator Access] permissions. The taskType determines how the Tasks are executed, there are three options simple, background and foreground. Our script will run in the background, this will allow us to RDP into our instance and check the Logs to ensure everything installed properly to test our deployment.

In the Service Definition above we’ve added some additional folders to our deployment, this is where we will be placing our website [in our case, we’re simply going to add an index.php file]. Within the Deployment Folder, add a new folder called Websites, within the new Websites folder, create a folder called MyPHPApp [or whatever you would like it named, be sure to modify the physicalDirectory attribute with the folder name].

Create a Websites Folder in the Deployment FolderCreate a MyPHPApp Folder within the Websites Folder

Now that our directories have been added, create a new file named index.php within the MyPHPApp folder and add the lines of code below.

<?php

phpinfo();

?>

Creating a Windows Azure Service Configuration

Now that we have a Service Definition to define the hard-requirements of our Service, we need to create a Service Configuration file to define the soft-requirements of our Service.

Microsoft has provided a way of creating a Service Configuration from our Service Definition to ensure we don’t miss any required elements.

If you intend to work with Windows Azure Tools on a regular basis, I would suggest adding the Path to the tools to your System Path, you can do this by executing the following script in a console window.

path=%path%;%ProgramFiles%\Windows Azure SDK\v1.3\bin;

We’re going to be using the CSPack tool to create our Service Configuration file. To Generate the Service Configuration we’ll need to open a console window and navigate to our project folder. Then we’ll execute the following command to create our Service Configuration (.cscfg) file.

cspack ServiceDefinition.csdef /generateConfigurationFile:ServiceConfiguration.cscfg

After you run this command take a look at your project folder, it should look relatively close to this:

Project after running CSPack to Create Configuration File

You’ll notice that executing CSPack has generated two files. First, It has generated our Service Configuration file, which is what we’re interested in. However, the tool has also gone and compiled our project into a Cloud Service Package (.cspkg) file, which is ready for deployment to Windows Azure [we’ll get back to the Cloud Service Package in the next post in this series]. Let’s take a look at the Configuration file.

<?xml version="1.0"?>
<ServiceConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" serviceName="PHPonAzure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
  <Role name="Deployment">
    <ConfigurationSettings>
      <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled" value="" />
      <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername" value="" />
      <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword" value="" />
      <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration" value="" />
      <Setting name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled" value="" />
    </ConfigurationSettings>
    <Instances count="1" />
    <Certificates>
      <Certificate name="Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" thumbprint="0000000000000000000000000000000000000000" thumbprintAlgorithm="sha1" />
    </Certificates>
  </Role>
</ServiceConfiguration>

Where did all of this come from? Let’s look at a simple table, that matches up how these settings relate to our Service Definition.

Service Definition Snippet

Service Configuration Snippet

<ServiceDefinition name=”PHPonAzure”/> <ServiceConfiguration serviceName=”PHPonAzure”/>
<WebRole name=”Deployment”/> <Role name=”Deployment”/>
<Import moduleName=”RemoteForwarder”/> <Setting name=”RemoteForwarder.Enabled”/>
<Import moduleName=”RemoteAccess”/> <Setting name=”RemoteAccess.Enabled”/>

<Setting name=”RemoteAccess.AccountUsername”/>

<Setting name=”RemoteAccess.EncryptedPassword”/>

<Setting name=”RemoteAccess.AccountExpiration”/>

<Certificate name=”RemoteAccess.PasswordEncryption”/>

For more information on the RemoteAccess and RemoteForwarder check out the series that I did on RDP in a Windows Azure Instance. These posts will also take you through the instructions on how to provide proper values for the RemoteAccess and RemoteForwarder Elements that were Generated by the Import statements in the ServiceDefinition.

  1. Upload a Certification to an Azure Hosted Service
  2. Setting up RDP to a Windows Azure Instance: Part 1
  3. Setting up RDP to a Windows Azure Instance: Part 2
  4. Connecting to a Windows Azure Instance via RDP

There are two additional attributes in which I would recommend adding to the ServiceConfiguration Element, osFamily and osVersion.

osFamily="2" osVersion="*"

These attributes will change the underlying Operating System Image that Windows Azure runs to Windows Server 2008 R2 and sets your Role to automatically update to the new image when available.

You can control the number of instances of your Roles are deployed by changing the value of the count attribute in the Instances Element. For now we’ll leave this value at 1, but keep in mind that Microsoft’s SLA requires 2 instances of your role to be running in order to guarantee 99.95% uptime.

Great Resources

Conclusion

In this entry we created both a Service Definition and a Service Configuration. Service Definitions provide information to the Fabric Controller to create non-changeable configurations to a Windows Azure Role. The Service Configuration file will provide additional information to the Fabric Controller to manage aspects of the environment that may change over time. In the next post we will be reviewing the Cloud Service Package and Deploying our Cloud Service into the Windows Azure Environment.

Happy Clouding!