Calling Python from PHP in Azure Websites

I was recently asked for help by an ISV. They needed to generate a SAS (Shared Access Signature) to grant time-limited access to resources that they were storing in Azure Blob Storage. They had developed their solution using PHP and deployed it to Azure Websites. I had generated SAS before using C# and .NET but never using PHP. No problem I thought … we have an Azure SDK for PHP and it’s available on GitHub !

Azure SDK for PHP

But after pouring over the code I came to the realisation that there is no functionality to generate a SAS for Blob Storage in the PHP SDK. It seems as though this was first opened as an issue in 2012 but has not been resolved. So what to do?

I didn’t want to implement a solution that would place the burden of keeping up to date with Azure changes on the ISV. I had a quick look at the Python SDK and the weight lifted off my shoulders when I found sharedaccesssignature.py !!

Azure SDK for Python

I verified my approach with the Azure SDK team and got the thumbs up. I was good to go and I could sense the solution would be a matter of minutes away – little did I know …

Test Azure Website

I created a test Website in Azure called callingpythonfromphp and ensured that PHP was enabled (it is by default). I didn’t need to change the Python version setting since Python is installed by default (as are all the other languages). The switches you can see below enable http handlers for the respective languages.

Site Settings for callingpythonfromphp Website

You can check that Python is installed via the Debug Console in Kudu. Here you can see that Python 2.7 is available in D:\Python27.

Kudu - console

I added two files to the D:\home\site\wwwroot folder of my Website – generate-fakesas.py and test-fakesas.php.

Listing of wwwroot

The test-fakesas.php file simply called Python and passed it the generate-fakesas.py script to execute.

<?php echo ">> "; system('D:\Python27\python.exe generate-fakesas.py'); echo " <<"; ?>;

The generate-fakesas.py was so named since all I’m doing is returning the string “SAS” and not actually generating a SAS. This will be output in the PHP script. In the final version it can be assigned to a PHP variable and utilised.

print ("SAS")

This was a quick test to check whether or not I could call a Python script from PHP in an Azure Website. The >> and << in the PHP script would allow me to visually confirm that the output of the Python script had been inserted where I expected.

Hitting the test-fakesas.php script via the Debug Console in Kudu gave me the expected result. I could see the SAS being output. I had successfully called into Python from the PHP script.

Execute PHP script

I then hit the test-fakesas.php script from the browser. Hmm – this was not good. The SAS string was nowhere to be seen.

Calling php script from Website

Looking at the php_errors.log file in D:\home\LogFiles I found the following:

PHP Warning: system(): Unable to fork [D:\Python27\python.exe generate-fakesas.py] in D:\home\site\wwwroot\test-fakesas.php on line 1

Errors in php_errors.log

The Fix

So there was a difference in behaviour between the Debug Console and the manner in which php-cgi was being launched. After a few emails back and forth to the Azure Websites team, they discovered that the source of this issue was to do with the fastcgi.impersonate PHP setting. It was set to 1 by default and needed to be switched off (set to 0). This resulted in the following solution on the Kudu Xdt Transform Samples wiki page.

Custom php.ini

This solution basically allows you to deploy a custom php.ini file for your Azure Website and override settings. When you are doing this ensure that ALL instances of fastcgi.impersonate=1 are changed to fastcgi.impersonate=0 and are NOT commented out.

Note that the applicationhost.xdt and php.ini file should be deployed to your D:\home\site folder.

Solution deployed

And now the call works from both the Debug Console in Kudu and the browser.

Finally - it works !

Ok – so that’s all great. But I actually needed to generate a SAS.

Generating a genuine SAS

DISCLAIMER – I’m going to preface this entire section by saying that I am not a Python guru.

The recommended mechanism for installing Python packages is pip but when I ran pip via the Debug Console in Kudu to install the Azure SDK …

D:\Python27\Scripts\pip.exe install azure

I got the following error.

error: could not create 'D:\Python27\Lib\site-packages\azure': Access is denied

I found that the only way I could install the Python Azure SDK via pip into the Azure Website was by starting off with Python’s virtualenv. I found a great blog post that got me up and running quickly. I created a new development environment called myapp.

D:\Python27\Scripts\virtualenv --no-site-packages myapp

Create development environment

This creates an entire new and isolated Python environment and copies utilities and the Python executables into this environment.

Next activate the development environment.

myapp\Scripts\activate

Activate development environment

And then install the Azure SDK.

pip install azure

Install Azure SDK

I added the test-sas.php file to the D:\home\site\wwwroot\folder of my Website and the  generate-sas.py file to the D:\home\site\wwwroot\myapp folder .

Python and PHP scripts

The test-sas.php file simply called Python and passed it the generate-sas.py script to execute. You can see that it is using the Python executable in the myapps development environment. This means it will also have access to the Azure SDK I installed.

<?php echo ">> "; system('myapp\Scripts\python myapp\generate-sas.py'); echo " <<"; ?>;

The generate-sas.py was based on an example from StackOverflow. The script now will actually generate a SAS for read-only access to the images/flower.png blob within my imagesstoragepb storage. This SAS will be output in the PHP script.

from azure.storage import *
accss_plcy = AccessPolicy()
accss_plcy.start = '2015-01-08'
accss_plcy.expiry = '2015-01-10'
accss_plcy.permission = 'r'
sap = SharedAccessPolicy(accss_plcy)
sas = SharedAccessSignature('imagesstoragepb', 'DPnYqQIKTbNn6iC+nH03wjbvHcpm9htIYVZYkGQJEaWhUbEaGj6koypC0R8AW5Zc6L/g8Sj1tmTPMQlWY8m+NQ==')
qry_str = sas.generate_signed_query_string('images/flower.png','b', sap)
print (sas._convert_query_string(qry_str))

Running this in the browser led to the successful generation of the expected SAS !!

Generated a SAS from PHP via Python in Azure

So don’t be put off if a feature you need is not available in the PHP SDK. Check if you can leverage the Python SDK. And if you need to override the default PHP settings in Azure Websites you also now have a mechanism to do that.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s