Programmatically upload and configure a SSL certificate for an Azure Cloud Service Deployment

Our team has recently been working on the Service Gateway project- its available in the Microsoft Azure Web Sites gallery so you can try it out!

Gallery

Anyhow back to the post. One of the recent features I implemented was HTTPS support for the actual Gateway Cloud Service project deployment. Now for a typical Cloud Service deployment simply configuring your cloud project in Visual Studio to add a HTTPS endpoint on 443 then choosing a certificate is adequate and easy. In the case of the Service Gateway Management Console we are actually providing a web portal experience that allows end users to provision a Cloud Service in template form and in this case each user will want to provide their own certificates for their own gateway domain. To do that required a little bit of work using the Microsoft Azure Management Libraries, a certificate *.pfx + password provided by the user and a template *.cspkg and *.cscfg.  This blog will share the key code snippets you will require, of course the full code is available on http://sg.codeplex.com

Create a cloud service project template

HttpsCloudProject

Configure a https endpoint and port binding


HttpsEndpoint Set the certificate thumbprint to 0′s, we will later replace this templated value with the uploaded certificate

<?xml version="1.0" encoding="UTF-8"?>
<ServiceConfiguration xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" serviceName="CloudProjectHttps" osFamily="3" osVersion="*" schemaVersion="2013-10.2.2">
   <Role name="Router">
      <Instances count="2" />
      <ConfigurationSettings>
         <Setting name="ConfigLocation" value="https://configtest.blob.core.windows.net/defaultconfig/v2/index.json" />
         <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=https;AccountName=your-account;AccountKey=your-key;" />
      </ConfigurationSettings>
      <Certificates>
         <Certificate name="CertificateName" thumbprint="0000000000000000000000000000000000000000" thumbprintAlgorithm="sha1" />
      </Certificates>
   </Role>
</ServiceConfiguration>

Implement code to retrieve the certificate thumbprint to be used in the *.cscfg and then upload the certificate. In the code listing below the deployment certificate is of type HttpPostedFileBase i.e file uploaded through an MVC app. For full code of the deployment class refer to the previously shared codeplex link

.....
   if(success && deployment.IsHttpsEnabled) 
   { 
       byte[] rawCert = await GetCertRawBytes(deployment.Certificate); 
       deployment.CertificateThumbprint = GetThumbprint(rawCert, deployment.CertificatePassword); 
       var uploadCertStatus = await UploadCertificateAsync(deployment.ServiceName, rawCert, deployment.CertificatePassword); 
       success &= uploadCertStatus.Success; await UpdateDeploymentProgressAsync(deployment, uploadCertStatus); 
   } 

.....
            
private async Task<byte[]> GetCertRawBytes(HttpPostedFileBase certificate) 
{ 
    byte[] rawCert; 
    using (var stream = certificate.InputStream) 
    { 
        rawCert = new byte[stream.Length]; 
        await stream.ReadAsync(rawCert, 0, rawCert.Length); 
    } 
    return rawCert; 
}

note the key store flags below are required to use X509Certificate2 on Microsoft Azure web sites


private string GetThumbprint(byte[] rawCert, string certPassword) 
{ 
   var x509 = new X509Certificate2(rawCert, certPassword, X509KeyStorageFlags.MachineKeySet); 
   return x509.Thumbprint; 
} 

To upload the actual certificate for the service you want to deploy you can use ComputeManagementClient from WAML. Note that its disposable so if your just newing it up in your method you should use it within a using. In the code below the private member variable is disposed by the enclosing class implementing IDisposable.

private async Task UploadCertificateAsync(string serviceName, byte[] rawCert, string certPassword = null) 
{ 
   var response = await _computeManagementClient.Value.ServiceCertificates.CreateAsync(serviceName, new ServiceCertificateCreateParameters() 
      { 
         CertificateFormat = CertificateFormat.Pfx, 
         Data = rawCert, 
         Password = certPassword.ToString() 
      }); 
} 

There is a lot going on in the following method, the key thing to note is that deployment.ServiceConfigTemplateLocation is just a url to a public blob that is the *.cscfg from above. i.e download it, replace all the template values

private async Task ProvisionCloudServiceAsync(Deployment deployment) 
{ 
   var status = new DeployStatus(); 
   status.Success = true; 

   using (var config = await _computeManagementClient.Value.HttpClient.GetAsync(deployment.ServiceConfigTemplateLocation)) 
   { 
      if (config.IsSuccessStatusCode) 
      { 
         using (var configStream = await config.Content.ReadAsStreamAsync()) 
         { 
            //Update the service configuration *.cscfg for the specifics for this deployment 
            var document = XDocument.Load(configStream); 
            var svcConfig = document.Root; 
            var ns = (XNamespace)svcConfig.Attribute("xmlns").Value; 
            var instances = svcConfig.Element(ns + "Role").Element(ns + "Instances"); 
            var settings = svcConfig.Element(ns + "Role").Elements(ns + "ConfigurationSettings"); 
            var configLocation = settings.Descendants().First(x => x.Attribute("name").Value == "ConfigLocation"); 
            var diagnosticsConnection = settings.Descendants().First(x => x.Attribute("name").Value == "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"); 
            
            instances.SetAttributeValue("count", deployment.InstanceCount); 
            configLocation.SetAttributeValue("value", deployment.RoleIndexConfigLocation); 
            
            var storageAccountConnection = GetStorageAccountConnectionString(deployment.SelectedStorageAccount, deployment.StorageAccountKey); 
            diagnosticsConnection.SetAttributeValue("value", storageAccountConnection); 

            //upload to storage account 
            var package = await CopyDeploymentPackageAsync(deployment, CloudStorageAccount.Parse(storageAccountConnection), "deployments", _maxCopyRetries, _copyRetrySleepPeriod);
            deployment.ServicePackageFileLocation = package.Uri.ToString(); 

            if(deployment.IsHttpsEnabled) 
            { 
               var certificates = svcConfig.Element(ns + "Role").Elements(ns + "Certificates"); 
               var thumprintSetting = certificates.Descendants().First(x => x.Attribute("name").Value == "CertificateName"); 
               thumprintSetting.SetAttributeValue("thumbprint", deployment.CertificateThumbprint); 
            } 

            try 
            { 
               await UpdateDeploymentProgressAsync(deployment, new DeployStatus(string.Format("Deploying Cloud Service '{0}'", deployment.ServiceName))); 
               
               //deploy the cloud service into the provisioned slot using the uploaded *.cspkg and *.cscfg 
               var response = await _computeManagementClient.Value.Deployments.CreateAsync(deployment.ServiceName, DeploymentSlot.Production, new DeploymentCreateParameters 
                              { 
                                 Label = deployment.ServiceName, 
                                 Name = deployment.ServiceName, 
                                 PackageUri = package.Uri, 
                                 Configuration = document.ToString(), 
                                 StartDeployment = true, 
                              });
               status.Success = response.StatusCode == System.Net.HttpStatusCode.OK && response.Error == null; 
               
               if(!status.Success) 
               { 
                  status.Message = string.Format("Error Deploying Cloud Service '{0}', \r\n Response Code {1} \r\n Error Code: {2} \r\n Error Message: {3} ", deployment.ServiceName, response.StatusCode, response.Error.Code, response.Error.Message); 
               } 
            } 
            catch (Exception ex) 
            { 
               Debug.WriteLine(ex.ToString()); status.Success = false; status.Message = string.Format("Error Deploying Cloud Service '{0}', \r\n Error Message: {1}", deployment.ServiceName, ex.Message);
            } 
         } 
      } 
      else 
      { 
         status.Success = false; status.Message = string.Format("Unable to locate Service Configuration template at {0}, On requesting template response status code is {1}", deployment.ServiceConfigTemplateLocation, config.StatusCode); 
      } 
   } 

   if (status.Success) 
   { 
      status.Message = string.Format("Finished Deployment of Cloud Service '{0}' successfully", deployment.ServiceName); 
   } 
   
   return status; 
} 

That’s about it for uploading a certificate for your cloud service then dynamically updating the service config for the thumbprint of an arbitrary certificate provided by the user. I think it would probably be useful to pull together a detailed post on how the deployment of the cloud service works as well.

To learn more about the Microsoft Azure Management Libraries checkout Microsoft Azure Management Libraries on NuGet and my colleague Brady Gasters Blog

Hope this helps,
Nick Harris

How to develop Windows 8.1 Apps with Windows Azure Mobile Services

If your just getting started developing your first Windows 8.1 application or if you are already a pro this recent session I gave down in New Zealand is worth a watch. I pulled it together when I was starting to feel a little bored of slide heavy presentations so instead I took a hands on approach and limited the deck to about 8 slides and spent the majority of the hour in Visual Studio. It was a fun session and I covered a lot of common scenarios you will encounter when building connected Windows 8.1 apps with Mobile Services including:

  • Getting started with an Azure Mobile Service backend
    • Creating your first mobile service
    • enabling Source control
    • npm install
  • Building Geospatial apps – how to implement
    • Where am I
    • Save a point of interest to your backend
    • Perform radial search of points of interest nearby
  • Auth – how to secure your applications
  • Media – how to implement
    • Media capture on the device
    • Securely uploading your media to Windows Azure Blob Storage using a Shared Access Signature
  • Notifications and Live Tiles – how to implement
    • Discussion on Push notifications verse Periodic notifications and when to choose which
    • Implement Push Notifications with the new Push Wizard in the VS 2013 tooling for Mobile Services
    • Implement Periodic notifications with Custom API

I hope you enjoy the session as much as I did, enjoy! nick

You can also watch it on Channel 9

How to handle WNS Response codes and Expired Channels in your Windows Azure Mobile Service

When implementing push notification solutions for your Windows Store apps many people will implement the basic flow you typically see in a demo then consider their implementation as job done. While this works well in demos and apps that are running at a small scale with few users those that are successful will likely want to optimize their solution to be more efficient to reduce compute and storage costs. While this post is not a complete guide I am providing it to at least give you enough information to get you thinking about the right things.
A few quick up front questions you should ask yourself are:

  • Is push appropriate? Should I be using Local, Scheduled or Periodic notifications instead?
  • Do I need updates at a more granular frequency then every 30 minutes?
  • Am I sending notifications that are personalized for each recipient or am I sending a broadcast style notification with the same content to a group of users?

A typical implementation

If you figured out push is the right choice and implemented the basic flow it will normally look something like this:

 

  1. Requesting a channel
  2. using Windows.Networking.PushNotifications;
    …
    var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
    
  3. Registering that channel with your cloud service
  4. var token = Windows.System.Profile.HardwareIdentification.GetPackageSpecificToken(null);
    string installationId = Windows.Security.Cryptography.CryptographicBuffer.EncodeToBase64String(token.Id);
    
    var ch = new JObject();
    ch.Add("channelUri", channel.Uri);
    ch.Add("installationId", installationId);
    
    try
    {
       await App.pushdemoClient.GetTable("channels").InsertAsync(ch);
    }
    catch (Exception exception)
    {
       HandleInsertChannelException(exception);
    }
    
  5. Authenticate against WNS and sending a push notification
  6. //Note: Mobile Services handles your Auth against WNS for your, all you have to do is configure your Store app and the portal WNS credentials.
    
    function SendPush() {
        var channelTable = tables.getTable('channels');
        channelTable.read({
            success: function (channels) {
                channels.forEach(function (channel) {
                    push.wns.sendTileWideText03(channel.uri, {
                        text1: 'hello W8 world: ' + new Date().toString()
                    });
                });
            }
        });
    }
    

And for most people this is about as far as the push implementation goes. What many are not aware of is that WNS actually provides two useful pieces of information that you can use to make your push implementation more efficient – a Channel Expiry time and a WNS response that includes both notification status codes and device status codes using this information you can make your solution more efficient. Let’s take a look at the first one

Handling Expired Channels

When you request a channel from WNS it will return to you both a channel and an expiry time, typically 30 days from your request. Consider that your app over time is popular but you do have users that over time end up deciding either to not use your app for extended periods or delete it all together. Over time these channels will hit their expiry date and will no longer be of any use and there is no need to a) send notifications to these channels and b) keep these channels in your datastore. Let’s have a look at a simple implementation that will cleanup your expired channels.

Using a Scheduled Job in Mobile Services we can perform a simple clean of your channels but first we must send the Expiry to your Mobile Service. To do this you must update step 2 of the above implementation to pass up the channel expiry as a property in your JObject – you will find the channel expiration in the channel.ExpirationTime property. For my channel table I have called this Expiry.

Following that once you have created your scheduled push job at say an interval of X (I am using 15 minutes) you can then add a function that deletes the expired channels similar to the following

function cleanChannels() {
    var sql = "DELETE FROM channels WHERE Expiry < GetDate()";
    mssql.query(sql, {
        success: function (results) {
            console.log('deleting expired channels:', results)
        },
        error: function (error) {
            console.log(error);
        }
    });
}

As you can see there is no real magic here and you probably want to handle UTC dates – but in short it demonstrates the concept that the expired channels are not useful for sending notifications so delete them, flag them as deleted or anything else that keeps them out of the valid set that you will push to… moving on

Handling the WNS response codes

When you send a notification to a channel via WNS, WNS will send a response. Within this response are many useful response headers. Today i’ll just focus on X-WNS-NotificationStatus but it’s worth noting that you should also consider X-WNS-DeviceConnectionStatus – more details here

Let’s look at a typical response:

{
headers:
{ ‘x-wns-notificationstatus’: ‘received’,
‘x-wns-msg-id’: ’707E20A6167E338B’,
‘x-wns-debug-trace’: ‘BN1WNS1011532′ },
statusCode: 200,
channel: ‘https://bn1.notify.windows.com/?token=AgYAAACghhaYbZa4sqjJ23pWp3kGDcEOEb3JxxdeBahCINn15fc11TiG0mlTpR5heYQmEaQrgZc3TSSwoUllW9s4Lsn3eyvSn19DcrX%2bOvSOY4Bq%2bPKGWbdy3mjTmaRi2Yb1dIM%3d’
}

Of interest in this response is X-WNS-NotificationStatus which can be one of three different states:

  • received
  • dropped
  • channelthrottled

As you can probably guess if you are sending notifications when you are either throttled or dropped it is probably not a good use of your compute power and as such you should really handle channels that are not returning received status in a fitting way. Consider the following when the scheduled job runs delete any expired channels and send notifications to channels in (the status of received) OR (that are not in the status of received AND that last had a push sent over an hour ago). This can be easily achieved by tracking the X-WNS-NotificationStatus every time a notification is sent. Code follows:

function SendPush() {
    cleanChannels();
    doPush();
}

function cleanChannels() {
    var sql = "DELETE FROM channel WHERE Expiry < GetDate()";

    mssql.query(sql, {
        success: function (results) {
            console.log('deleting expired channels:', results)
        },
        error: function (error) {
            console.log(error);
        }
    });
}

function doPush() {
    //send only to received channels OR channels that are not in the state of received that last had a push sent over an hour ago 
    var sql = "SELECT * FROM channel WHERE notificationStatus IS NULL OR notificationStatus = 'received' 
                    OR ( notificationStatus <> 'received'
                           AND CAST(GetDate() - lastSend AS float) * 24 >= 1) ";

    mssql.query(sql, {
        success: function (channels) {
            channels.forEach(function (channel) {

                push.wns.sendTileWideText03(channel.uri, {
                    text1: 'hello W8 world: ' + new Date().toString()
                }, {
                    success: function (response) {
                        handleWnsResponse(response, channel);
                    },
                    error: function (error) {
                        console.log(error);
                    }
                });
            });
        }
    });
}

// keep track of the last know X-WNS-NotificationStatus status for a channel
function handleWnsResponse(response, channel) {
    console.log(response);

    var channelTable = tables.getTable('channel');

    // http://msdn.microsoft.com/en-us/library/windows/apps/hh465435.aspx
    channel.notificationStatus = response.headers['x-wns-notificationstatus'];
    channel.lastSend = new Date();

    channelTable.update(channel);
}

That’s about it I hope this post has helped you to start thinking about how to handle your Push Notification implementation beyond the basic 101 demo push implementation

Enjoy, Nick Harris

Build Websites and Apache Cordova/PhoneGap apps using the new HTML client for Azure Mobile Services

Today Scott Guthrie announced HTML client support for Windows Azure Mobile Services such that developers can begin using Windows Azure Mobile Services to build both HTML5/JS Websites and Apache Cordova/PhoneGap apps.

The two major changes in this update include:

  • New Mobile Services HTML client library that supports IE8+ browsers, current versions of Chrome, Firefox, and Safari, plus PhoneGap 2.3.0+.  It provides a simple JavaScript API to enable both the same storage API support we provide in other native SDKs and easy user authentication via any of the four supported identity providers – Microsoft Account, Google, Facebook, and Twitter.
  • Cross Origin Resource Sharing (CORS) support to enable your Mobile Service to accept cross-domain Ajax requests. You can now configure a whitelist of allowed domains for your Mobile Service using the Windows Azure management portal.

With this update Windows Azure Mobile Services now provides a scalable turnkey backend solution for your Windows Store, Windows Phone, iOS, Android and HTML5/JS applications.

HTMLClient

To learn more about the new HTML client library for Windows Azure Mobile Services please see checkout the new HTML tutorials on WindowsAzure.com and the following short 4 minute video where Yavor Georgiev demonstrates how to quickly create a new mobile service, download the HTML client quick start app, run the app and store data within the Mobile Service then configure a custom domain with Cross-origin Resource Sharing (CORS) support

Watch on Channel9 here

If you have any questions please reach out to us via dedicated Windows Azure Mobile Services our forum.

Enjoy,

Nick Harris

Building Mobile Apps with Windows Azure Content from BUILD 2012

//BUILD 2012 was an awesome event! this post is a little late.  Although late this content is still extremely relevant if you are building Connected Mobile Apps. Here are a couple of sessions you should watch:

Keynote Demo of Windows Azure Mobile Services
During //BUILD 2012 I was fortunate enough to be to be on point for delivering the day 2 Mobile Services keynote demo app Event Buddy.  If you have not watched this keynote demo I would recommend you check it out – the Mobile Services Demo starts about 10mins 30 seconds in


Direct Video link on Channel9.  Event Buddy is now also available as a Code Sample that you can download here.

Developing Mobile Solutions on Windows Azure – Part I

Watch Josh take a Windows Phone 8 + Windows Store application and light it up with cloud services to handle data, authentication and push notifications – right before your eyes with Windows Azure Mobile Services. Almost all demo and no slides, this session is designed to take you from zero to Mobile Services here in 60 minutes.

You can also watch this video directly on Channel 9 here.
Developing Mobile Solutions on Windows Azure – Part I

In addition to this I also presented with fellow baldy – Chris Risner.  In this session we took the output of Part I  from Josh and demonstrated how you could extend your existing applications to support common scenarios such as geo-location, media, and cloud to device messaging using services from Windows Azure.   Here a summary of the content of this presentation that I humbly grabbed directly from a prior post by Chris

  • We took pictures and uploaded them to Blob Storage.  For this we used a web service layer, running in Windows Azure Websites, to get a SAS (Shared Access Signature) which allowed us to securely upload to blob storage.
  • We then got the location of the device and used that information to geo-tag the pictures we just uploaded.
  • We added a web page to our web service which allowed a user to select a geographical area on a map and request a push notification be sent to anyone that had taken a picture inside of it (this used the Bing Maps API and Queues from Windows Azure Storage)
  • We deployed a worker role to Windows Azure Cloud Services which would check the queue and then figure out who should be notified (using Entity Framework’s geospatial support) and sent out the actual push notifications to both the Windows 8 and Windows Phone 8 clients.

Here is the direct link to watch on Channel 9 and Chris has also made the code available here
Enjoy,

Nick Harris

Top Ten Reasons to Compete in the Imagine Cup Windows Azure Challenge!

Have you checked out the Imagine Cup Windows Azure Challenge? It’s not too late! You’ve got until February 15 for someone on your team to pass the Windows Azure Challenge quiz, which is available from your Dashboard once you’ve signed up for the challenge and created a team.

We’re spending this week sharing information about the challenge and about developing for Windows Azure. Here’s my Top Ten Reasons to Compete in the Windows Azure Challenge:

  1. You get a Windows Azure account to build your submission for FREE!
  2. Much more power than your favorite superhero without the occupational risks!  From the safety of your couch with a click of a button you can quickly build, deploy, and manage applications across a global network of Microsoft-managed data centers.
  3. Born to code? Me too, we already have so much in common :)  - Windows, Mac, Linux - .NET, node.js, java, php, python and others you can build applications using any operating system, language or tool. Use what works best for you!
  4. When building your solutions you consider leveraging partners from the Windows Azure Store who provide both application and data services.  For example you could use Microsoft Translator to translate text and SendGrid to send an email.
  5. Need a Database? Apply your existing knowledge and hit the ground running using one of the following popular databases including Windows Azure SQL Database, MySQL and MongoDB.
  6. Building a distributed system? Check out Service Bus for relayed connectivity between distributed components anywhere in the globe!
  7. Got Video? Build media solutions using Windows Azure Media Services: Ingest, Encode, Protect and Stream media from the cloud with ease.
  8. Go Web go! With Windows Azure Web Sites there is no easier way to get a web site up and running in seconds. Before you could read a whole paragraph on how to use Web Sites, you could have a site up and running with a database back-end.   Develop with ASP.NET, PHP or Node.js and deploy in seconds with FTP, Git or TFS.
  9. Got Mobile? When competing in a world class challenge every minute counts.  Within seconds you can use Windows Azure Mobile Services to create a back-end for your Windows Store + Windows Phone apps to incorporate structured storage, user authentication, and push notifications.

And the number one reason to compete in the Windows Azure Challenge is . . .

  1. Got Job? huh… wait!, what now? When the competition is all done and dusted, the skills you’ve gained working with Windows Azure will set you well apart from the rest when it comes to looking for your dream job – it did for me!

Nick Harris | Imagine Cup Windows Azure Challenge Captain | @cloudnick