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

Opsgility announces instructor led Windows Azure Training

I am excited to hear a good old friend of mine from Microsoft Michael Washam has started a new company, www.opsgility.com, focused on instructor led Windows Azure training.   They are starting open enrollment classes now in the United States and will also do private deliveries (US and International).

If you have training needs and are interested in talking to them give them a call at: +1 866-833-3878 or email: info@opsgility.com

Updated NotificationsExtensions WnsRecipe Nuget to support Windows 8.1 templates now available

A short post to let you know that I have just published the updated NotificationsExtensions WnsRecipe Nuget with support for the new notification templates that were added in Windows 8.1.

Here is a short demonstration of how to use it to send a new TileSquare310x310ImageAndText01 template with the WnsRecipe Nuget Package

Install the package using Nuget Package Manager Console. (Note you could also do this using Manage package references in solution explorer)

install-package WnsRecipe

Add using statements to the NotificationsExtensions namespace

using NotificationsExtensions;
using NotificationsExtensions.TileContent;

New up a new WnsAccessTokenProvider and provide it your credentials configured in the Windows Store app Dashboard

private WnsAccessTokenProvider _tokenProvider = new WnsAccessTokenProvider("ms-app://", "");

Use Tile Content Factory to create your tile template

var tile = TileContentFactory.CreateTileSquare310x310ImageAndText01();

tile.Image.Src = "https://nickha.blob.core.windows.net/tiles/empty310x310.png";
tile.Image.Alt = "Images";
tile.TextCaptionWrap.Text = "New Windows 8.1 Tile Template 310x310";

// Note you really should not do the line below :), 
// instead you should be setting the required content 
// through property tile.Wide310x150Content so that users
// get updates irrespective of what size tile they have pinned to Start
tile.RequireWide310x150Content = false;  

//Send the notification to the desired channel
var result = tile.Send(new Uri(channel), _tokenProvider);

and here is the output
310x310tile

Enjoy, Nick Harris

How to implement Periodic Notifications in Windows Store apps with Azure Mobile Services Custom API

I blogged previously on how to make your Push Notification implementation more efficient.  In this post I will detail an alternative to Push, that is Periodic Notifications, which is are a poll based solution for updating your live Tiles and Badge content (Note that it can’t be used for Toast or Raw). It turns out if your app scenario can deal with only receiving notifications every thirty minutes or more that this is a much easier way for you to update your tiles.  All you need to do is configure you app for periodic notifications, and point it at a service API that will return the appropriate XML template for the badge or tile update in your app whether you are using WCF, Web API or others this is quite an easy thing to achieve.  In this case I will demonstrate how you can implement this using Mobile Services.

Let’s start with the backend service by creating a Custom API.  To do this all you need to do is select the API tab in the Mobile Services portal.

MobileServices_CustomAPITab Next provide a name for your endpoint and set the permissions for get to everyone

MobileServices_CustomAPIDialog

Next define the return XML return payload for your custom API.  You can see examples of each different Tile templates here

exports.get = function(request, response) {
    // Use "request.service" to access features of your mobile service, e.g.:
    //   var tables = request.service.tables;
    //   var push = request.service.push;
 
    response.send(200, ''+
                            ''+
                                ''+
                                    ''+
                                    '@ntotten enjoying himself a little too much :)'+
                                ''+
                            ''+
                        '');
};

    Note:

  • If you are supporting multiple tile sizes you should sending the whole payload in for each of the varying tile sizes
  • This is just an example ? – you really should be providing dynamic content here rather than the same tile template every time

Next configure you’re app to point at your Custom API through the package.appxmanifest

VisualStudio2013_PackageManifestPeriodicNotifications

Run your app, ensure your app is pinned to start in the right dimension for the content you are returning e.g

Windows_PinLargeTile

Pin your tile and wait for the update after app has been run once.

Windows_LargeTileUpdatedByPeriodicNotification

Job done! – the tile will be updated with the content from your site with the periodic update per your package manifest definition.

Enjoy, Nick Harris

BUILD 2013 session recap

//BUILD 2013 was an awesome event! this post is a little late (I had a bunch more events back to back right after this one and I am finally digging out :) . Although late this content is still extremely relevant if you are building Connected Mobile Apps. If your interested in building mobile apps you should checkout some of the content I contributed to build this year.

Keynote Demo of Windows Azure Mobile Services

For //BUILD 2013 I was fortunate enough to be on point for delivering the Day 2 Windows Azure Mobile Services keynote content. If you have not watched this keynote I would recommend you check it out – the Windows Azure Mobile Services Demo starts about 31mins 30 seconds in

Watch the direct from Channel9 here

Build Connected Windows 8.1 Apps with Mobile Services

Want less slides and more live action? Me too! Come join me in this session where you’ll learn how to develop XAML/C# Windows Store apps that take advantage of Mobile Services as a cloud backend. During this session we’ll leverage a number of the Windows Runtime APIs used for Geolocation, Media capture and Notifications. Following this I’ll demonstrate how you can take advantage of Mobile Services to store your geospatial data, media, send notifications and auth users of your service using popular social identity providers.

Download slides and/or watch direct from Channel9 here

Enjoy, Nick Harris

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