FabrikamShipping: the provisioning machinery built with Windows Azure

This is a series of posts, read the introduction to FabrikamShipping for more information about this sample application and Vittorio’s blog for latest updates.

One of the first challenges we had to solve in FabrikamShipping was the provisioning/on boarding of new customers. If you look at FabrikamShipping subscription page you can see there are 3 different subscription types: Personal, Small Business and Enterprise. Each subscription type consists of different input, output and processing steps. However, there are aspects that are shared between them, for instance sending an email to the customer when we’re done, create the subscription info, create rules in Access Control Service, etc.

We wanted to have something lightweight that could run in a Windows Azure worker role waiting for queue messages and perform a set of tasks. Keep reading to understand what this is about….

Provisioning API

With those things in mind we wanted a Provisioning API would allow us:

  • to define different type of provisioning flows (ie: small biz has a different provisioning workflow compared to enterprise)
  • to handle manual steps (for things that don’t have an API)
  • to compensate in case of failure
  • to retry a failed provisioning

On the non-functional side we wanted:

  • Reusability and composability of tasks between the different workflows
  • Resiliency
  • Nice DX (developer experience)
  • Testability

With that in mind and based on previous experiences in BidNow and the patterns & practices cloud guide, we sketched the following API:

Task.TriggeredBy(Message.In("deploy-queue"))
    .Do(
        new CreateSelfSignedCert(),
        new CreateSqlAzureDb(),
        new ConfigurePackageAndMetadata(),
        new SetupFederation(),
        new DeployApp() });

Which in essence is:

Task.TriggeredBy(() => condition)
    .Do(params ICommand[] commands)
And allow us to define time based conditions like
Task.TriggeredBy(Schedule.Every(30 * 1000))
    .Do(...);
After a couple of days we had a prototype working and this is what you can find today in the FabrikamShipping source code.
Task.TriggeredBy(Message.OfType<SmallBizProvisioningMessage>(new AzureQueue(account)))
    .SetupContext((message, context) =>
    {
        context.Add("TenantAlias", message.CompanyAlias);
        context.Add("SubscriptionId", message.SubscriptionId);
        context.Add("SmallBizApplicationUrl", smallBizApplicationUrl);
        context.Add("DatabaseName", "fs-" + message.CompanyAlias);
    })
    .Do(
        new UpdateSubscriptionStatus(...),
        new CreateSigningCertificate(...),
        new CreateSqlAzureDb(...),
        new CreateSqlAzureDb(...),
        new CreateUsers(...),
        new ConfigureTrustRelationshipForSmallBiz(...),
        new UploadTenantMetadata(...),
        new NotifyUserCreated(...)
    .OnError(logException)
    .Start();
These kind of workflows can be implemented with this API. This is the actual workflow for the FabrikamShipping enterprise provisioning and small business provisioning:
Enterprise ProvisioningSmall Business Provisioning
imageimage
So in short, the API supports the following
  • Queue message based triggers
  • Time based triggers for tasks that require manual or long running operations
  • Commands with Do and Undo for compensation and retry
  • Sharing context between commands
  • Handling errors

Commands

Every command derives from an interface ICommand that has Do and Undo and takes a dictionary that has contextual information. This is a sample command that deploys a package to a hosted service.
public class DeployAzureService : ICommand
{
      private readonly ProvisioningLogger logger;
      private readonly ServiceManagementWrapper azureApi;

      public DeployAzureService(ProvisioningLogger logger, ServiceManagementWrapper azureApi)
      {
          this.logger = logger;
          this.azureApi = azureApi;
      }

      public void Do(IDictionary<string, object> context)
      {
          // use azure Service Management API to deploy the package
      }

      public void Undo(IDictionary<string, object> context)
      {
         // remove the package
      }
}
There are commands for a bunch of things:
  • Create SQL Azure DB
  • Send emails
  • Create a trust relationship with Access Control Service
  • Create x509 certificates
  • Running SQL scripts
  • Create tenant metadata

Logging

You can also use a logger (see the ProvisioningLogger dependency on the command above) that will push log entries to an Azure table that later on we use to print a nice looking status for both the subscriber and the admin
image
If something went wrong you can click on View Log and look at the actual steps and values
image
Finally, this comes with the usual disclaimer. This is sample code and is provided as-is… But if you are an ISV creating apps in Azure, you should definitely check this out!



Leave a Reply