More Azure ASP.NET MVC…

In my previous two posts I showed how to setup your system for Azure and ASP.NET MVC Development and the basics of how to create an Azure ASP.NET MVC Project.

In this post, I will delve deeper into how to configure and deploy an Azure ASP.NET MVC Project.

From my previous post, my first project looked like this:

SimpleAzureSolution

In this solution, we have:

  • A Cloud Service project called FirstProject.
  • Two role projects:
    • An MVC Web Role project called FirstProject_MvcWebRole.
    • An Azure Worker Role project called FirstProject_WorkerRole.

Inside the Cloud Service project we have two important files:

ServiceDefinition.csdef
This file defines your service, and is deployed as part of the binaries of your service.  It cannot be changed without re-deployment.

The default Azure ServiceDefinition.csdef file looks like this:

 <?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="FirstProject" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WorkerRole name="Worker" />
  <WebRole name="Web">
    <InputEndpoints>
      <!-- Must use port 80 for http and port 443 for https when running in the cloud -->
      <InputEndpoint name="HttpIn" protocol="http" port="80" />
    </InputEndpoints>
  </WebRole>
</ServiceDefinition>

The key elements of this file you need to understand are:

  • The name="FirstProject" attribute names the service.
  • The  <WorkerRole name="Worker" /> element defines that this service contains a worker role.
  • The <WebRole name="Web"> element defines that the service contains a web role, and defines what inputs it will respond to.


ServiceConfiguration.cscfg
This file configures your service, and is deployed outside of the binaries of your service, so it can be changed while your service runs without re-deployment.

The default ServiceConfiguration.cscfg file looks like this:

 <?xml version="1.0"?>
<ServiceConfiguration serviceName="FirstProject" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
  <Role name="Worker">
    <Instances count="1" />
    <ConfigurationSettings />
  </Role>
  <Role name="Web">
    <Instances count="1" />
    <ConfigurationSettings />
  </Role>
</ServiceConfiguration>

At this point, we need to expand our service definition and configuration files to allow us to use Azure blob, table and queue storage services.

The first step in doing this is to add more configuration settings to our ServiceDefinition.csdef file:

 <?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="FirstProject" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WorkerRole name="WorkerRole">
    <ConfigurationSettings>
      <Setting name="AccountName"/>
      <Setting name="AccountSharedKey"/>
      <Setting name="BlobStorageEndpoint"/>
      <Setting name="QueueStorageEndpoint"/>
      <Setting name="TableStorageEndpoint"/>
      <Setting name="allowInsecureRemoteEndpoints"/>
    </ConfigurationSettings>
  </WorkerRole>
  <WebRole name="Web">
    <InputEndpoints>
      <!-- Must use port 80 for http and port 443 for https when running in the cloud -->
      <InputEndpoint name="HttpIn" protocol="http" port="80" />
    </InputEndpoints>
    <ConfigurationSettings>
      <Setting name="DevFabric"/>
      <Setting name="AccountName"/>
      <Setting name="AccountSharedKey"/>
      <Setting name="BlobStorageEndpoint"/>
      <Setting name="QueueStorageEndpoint"/>
      <Setting name="TableStorageEndpoint"/>
      <Setting name="allowInsecureRemoteEndpoints"/>
    </ConfigurationSettings>
  </WebRole>
</ServiceDefinition>

In these definitions, we’ve added two key security configuration settings:

  • AccountName – specifies your Azure storage account name.
  • AccountSharedKey – specifies your Azure storage account shared key.

And a number of endpoint settings:

  • BlobStorageEndpoint – the address/host name of the Azure blob storage endpoint.
  • QueueStorageEndpoint – the address/host name of the Azure queue storage endpoint.
  • TableStorageEndpoint – the address/host name of the Azure table storage endpoint.
  • allowInsecureRemoteEndpoints – a value which indicates whether to allow HTTP access by security-sensitive ASP.NET providers.

And, in the WebRole, we’ve added a setting named DevFabric (more on this later).

Now that we have defined that these settings exist in our ServiceDefinition.csdef file, it’s time to configure them for our actual service in our ServiceConfiguration.cscfg file.

Here’s an example of such a configuration that will work against development storage endpoints:

 <?xml version="1.0"?>
<ServiceConfiguration serviceName="FirstProject" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
  <Role name="WorkerRole">
    <Instances count="1" />
    <ConfigurationSettings>
      <Setting name="AccountName" value="devstoreaccount1"/>
      <Setting name="AccountSharedKey" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/>
      <Setting name="BlobStorageEndpoint" value="http://127.0.0.1:10000"/>
      <Setting name="QueueStorageEndpoint" value="http://127.0.0.1:10001"/>
      <Setting name="TableStorageEndpoint" value="http://127.0.0.1:10002"/>
      <Setting name="allowInsecureRemoteEndpoints" value="true"/>
    </ConfigurationSettings>
  </Role>
  <Role name="Web">
    <Instances count="1" />
    <ConfigurationSettings>
      <Setting name="DevFabric" value="true"/>
      <Setting name="AccountName" value="devstoreaccount1"/>
      <Setting name="AccountSharedKey" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/>
      <Setting name="BlobStorageEndpoint" value="http://127.0.0.1:10000"/>
      <Setting name="QueueStorageEndpoint" value="http://127.0.0.1:10001"/>
      <Setting name="TableStorageEndpoint" value="http://127.0.0.1:10002"/>
      <Setting name="allowInsecureRemoteEndpoints" value="true"/>
    </ConfigurationSettings>
  </Role>
</ServiceConfiguration>

You will notice that each thing defined in the ServiceDefinition.csdef is configured in the ServiceConfiguration.cscfg file.

This is where things become interesting because running an Azure service in the development fabric requires one set of configuration values, and running the same Azure service in the cloud requires a different set of configuration values.

When running an Azure service on the development fabric, we need:

 <ConfigurationSettings>
  <Setting name="AccountName" value="devstoreaccount1"/>
  <Setting name="AccountSharedKey" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/>
  <Setting name="BlobStorageEndpoint" value="http://127.0.0.1:10000"/>
  <Setting name="QueueStorageEndpoint" value="http://127.0.0.1:10001"/>
  <Setting name="TableStorageEndpoint" value="http://127.0.0.1:10002"/>
  <Setting name="allowInsecureRemoteEndpoints" value="true"/>
</ConfigurationSettings>

So our storage endpoints point to development storage, with a special AccountName and AcountSharedKey.

And when running the same Azure service in the cloud, we need something like:

 <ConfigurationSettings>
  <Setting name="AccountName" value="YourStorageAccountName"/>
  <Setting name="AccountSharedKey" value="YourStorageAccountSharedKey"/>
  <Setting name="BlobStorageEndpoint" value="http://blob.core.windows.net"/>
  <Setting name="QueueStorageEndpoint" value = "http://queue.core.windows.net"/>
  <Setting name="TableStorageEndpoint" value="http://table.core.windows.net"/>
  <Setting name="allowInsecureRemoteEndpoints" value="true"/>
</ConfigurationSettings>

So our storage endpoints point to cloud storage.  (*Don’t get hung up on “allowInsecureRemoteEndpoints” and not using “https” at this point, more in later posts.  Also, the values for “YourStorageAccountName” and “YourStorageAccountSharedKey” will come from the development portal.)

It’s at this point that we need to start to think about configuration management.

Deployment Scenarios

There are three deployment scenarios:

  1. Running entirely locally (development fabric and development storage).
  2. Running in what I call “hybrid” mode (development fabric and cloud storage).
  3. Running entirely in the cloud (cloud fabric and cloud storage, in other words, deployed).

And they require different configurations.

Enter configuration management…

Fortunately, there is a relatively easy solution to this problem, and it is to introduce multiple Azure Cloud Service projects into our solution to cover the different deployment scenarios.

To do this, we need to add two more Azure Cloud Service Projects to our solution, and rename/reconfigure our first Azure Cloud Service Project.

Here are the steps we need to take:

First, rename your Cloud Service Project to X_DevFabric (in this case, FirstProject_DevFabric) in preparation for making it the Cloud Service Project that is configured for deployment scenario #1 above - running entirely locally (development fabric and development storage):

RenameServiceProject

Next, add two more Azure Cloud Service Projects to your solution, and call them:

X_Cloud (in this case, FirstProject_Cloud)

AddCloudProject

X_Hybrid (in this case, FirstProject_Hybrid)

AddHybridProject

At this point, your solution will look something like:

ServiceProjectsAdded 

Once you’ve done this, edit the ServiceDefinition.csdef file in the X_Cloud, X_DevFabric and X_Hybrid projects, and paste the following settings into each, so they all look like this:

<?xmlversion="1.0" encoding="utf-8"?>

<ServiceDefinition name="FirstProject" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">

<WorkerRole name="WorkerRole">

<ConfigurationSettings>

<Settingname="AccountName"/>

<Settingname="AccountSharedKey"/>

<Settingname="BlobStorageEndpoint"/>

<Settingname="QueueStorageEndpoint"/>

<Settingname="TableStorageEndpoint"/>

      <Settingname="allowInsecureRemoteEndpoints"/>

</ConfigurationSettings>

</WorkerRole>

<WebRole name="Web">

<InputEndpoints>

      <InputEndpoint name="HttpIn" protocol="http" port="80" />

</InputEndpoints>

<ConfigurationSettings>

<Settingname="DevFabric"/>

<Settingname="AccountName"/>

<Settingname="AccountSharedKey"/>

<Settingname="BlobStorageEndpoint"/>

<Settingname="QueueStorageEndpoint"/>

<Settingname="TableStorageEndpoint"/>

      <Settingname="allowInsecureRemoteEndpoints"/>

</ConfigurationSettings>

</WebRole>

</ServiceDefinition>

This sets up the service definitions in each project so that they can be properly configured.

The next step is to add the Web and Worker roles to BOTH the X_Cloud and X_Hybrid Cloud Service Projects.  Do the following in both projects:

Add the Web Role:

AddWebRole 

AddWebRoleTo

And add the Worker Role:

AddWorkerRole

AddWorkerRoleTo 

(This is getting long, I know, but just keep on keeping on… Software development isn’t for wimps.)

OK, the next step is to edit the ServiceConfiguration.cscfg files in each of our Cloud Service Projects as follows:

For the X_Cloud project, the ServiceConfiguration.cscfg contains:

 <?xml version="1.0"?>
<ServiceConfiguration serviceName="FirstProject" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
    <Role name="WorkerRole">
        <Instances count="1" />
        <ConfigurationSettings>
            <Setting name="AccountName" value="YourStorageAccountName"/>
            <Setting name="AccountSharedKey" value="YourStorageAccountSharedKey"/>
            <Setting name="BlobStorageEndpoint" value="http://blob.core.windows.net"/>
            <Setting name="QueueStorageEndpoint" value = "http://queue.core.windows.net"/>
            <Setting name="TableStorageEndpoint" value="http://table.core.windows.net"/>
            <Setting name="allowInsecureRemoteEndpoints" value="true"/>
        </ConfigurationSettings>
    </Role>
    <Role name="Web">
        <Instances count="1" />
        <ConfigurationSettings>
            <Setting name="DevFabric" value="false"/>
            <Setting name="AccountName" value="YourStorageAccountName"/>
            <Setting name="AccountSharedKey" value="YourStorageAccountSharedKey"/>
            <Setting name="BlobStorageEndpoint" value="http://blob.core.windows.net"/>
            <Setting name="QueueStorageEndpoint" value = "http://queue.core.windows.net"/>
            <Setting name="TableStorageEndpoint" value="http://table.core.windows.net"/>
            <Setting name="allowInsecureRemoteEndpoints" value="true"/>
        </ConfigurationSettings>
    </Role>
</ServiceConfiguration>

For the X_DevFabric project, the ServiceConfiguration.cscfg contains:

 <?xml version="1.0"?>
<ServiceConfiguration serviceName="FirstProject" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
  <Role name="WorkerRole">
    <Instances count="1" />
      <ConfigurationSettings>
          <Setting name="AccountName" value="devstoreaccount1"/>
          <Setting name="AccountSharedKey" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/>
          <Setting name="BlobStorageEndpoint" value="http://127.0.0.1:10000"/>
          <Setting name="QueueStorageEndpoint" value="http://127.0.0.1:10001"/>
          <Setting name="TableStorageEndpoint" value="http://127.0.0.1:10002"/>
          <Setting name="allowInsecureRemoteEndpoints" value="true"/>
      </ConfigurationSettings>
  </Role>
  <Role name="Web">
    <Instances count="1" />
      <ConfigurationSettings>
          <Setting name="DevFabric" value="true"/>
          <Setting name="AccountName" value="devstoreaccount1"/>
          <Setting name="AccountSharedKey" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/>
          <Setting name="BlobStorageEndpoint" value="http://127.0.0.1:10000"/>
          <Setting name="QueueStorageEndpoint" value="http://127.0.0.1:10001"/>
          <Setting name="TableStorageEndpoint" value="http://127.0.0.1:10002"/>
          <Setting name="allowInsecureRemoteEndpoints" value="true"/>
      </ConfigurationSettings>
  </Role>
</ServiceConfiguration>

For the X_Hybrid project, the ServiceConfiguration.cscfg contains:

 <?xml version="1.0"?>
<ServiceConfiguration serviceName="FirstProject" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
    <Role name="WorkerRole">
        <Instances count="1" />
        <ConfigurationSettings>
            <Setting name="AccountName" value="YourStorageAccountName"/>
            <Setting name="AccountSharedKey" value="YourStorageAccountSharedKey"/>
            <Setting name="BlobStorageEndpoint" value="http://blob.core.windows.net"/>
            <Setting name="QueueStorageEndpoint" value = "http://queue.core.windows.net"/>
            <Setting name="TableStorageEndpoint" value="http://table.core.windows.net"/>
            <Setting name="allowInsecureRemoteEndpoints" value="true"/>
        </ConfigurationSettings>
    </Role>
    <Role name="Web">
        <Instances count="1" />
        <ConfigurationSettings>
            <Setting name="DevFabric" value="false"/>
            <Setting name="AccountName" value="YourStorageAccountName"/>
            <Setting name="AccountSharedKey" value="YourStorageAccountSharedKey"/>
            <Setting name="BlobStorageEndpoint" value="http://blob.core.windows.net"/>
            <Setting name="QueueStorageEndpoint" value = "http://queue.core.windows.net"/>
            <Setting name="TableStorageEndpoint" value="http://table.core.windows.net"/>
            <Setting name="allowInsecureRemoteEndpoints" value="true"/>
        </ConfigurationSettings>
    </Role>
</ServiceConfiguration>

At this point, we are ready to run our service in each of the deployment scenarios outlined above.

Caveat:

I am still researching <Setting name="DevFabric" value="false"/> entry. I cannot find definitive documentation on it at this time.  I suspect true when the web role is running in the development fabric, and false when the web role is deployed.  TBD.

There is a lot more to go, but this posting has brought us a lot of the way there.  In my next posting, I will cover CRUD (Create, Read, Update and Delete) operations in Azure storage running in all three deployment scenarios.

Best,

Brian