This is an ongoing series on Window’s Azure. The series starts here, and all code is located on GitHub:https://github.com/mmooney/MMDB.AzureSample.
Introduction
In the last post, we covered how to easily deploy a Azure Cloud Service using the new Sriracha 2.0 command line tools, specifically the Deploy Cloud Service Task.
In that post, we just covered the minimum amount of configuration to get a basic Cloud Service site up and running. In this post, we’ll cover the handful of more advanced configuration options that will hopefully make you’re live easier.
In the last post, we had a pretty basic configuration file:
{ "AzureSubscriptionIdentifier": "ThisIsYourAzureSubscriptionIdentifier", "AzureManagementCertificate" : "ThisIsYourAzureManagementCertificate", "ServiceName" : "AzureViaSriracha", "StorageAccountName" : "srirachademo", "AzurePackagePath": ".\\MMDB.AzureSample.Web.Azure\\bin\\Release\\app.publish\\MMDB.AzureSample.Web.Azure.cspkg", "AzureConfigPath": ".\\MMDB.AzureSample.Web.Azure\\bin\\Release\\app.publish\\ServiceConfiguration.Cloud.cscfg" }
For this post, we’re doing to add a bit more configuration to our project:
{ "AzureSubscriptionIdentifier": "ThisIsYourAzureSubscriptionIdentifier", "AzureManagementCertificate" : "ThisIsYourAzureManagementCertificate", "ServiceName" : "AzureViaSriracha", "StorageAccountName" : "srirachademo", "AzurePackagePath": ".\\MMDB.AzureSample.Web.Azure\\bin\\Release\\app.publish\\MMDB.AzureSample.Web.Azure.cspkg", "AzureConfigPath": ".\\MMDB.AzureSample.Web.Azure\\bin\\Release\\app.publish\\ServiceConfiguration.Cloud.cscfg" "RoleList": { "MMDB.AzureSample.Web": { "InstanceCount": 2, "ConfigurationSettingValues": { "TestSettingValue1": "Vote Quimby", "TestSettingValue2": "He'd vote for you" }, "CertificateThumbprints": { "MySSLCert": "ThisIsYourSSLThumbprint" } } } }
In this case, we’re not just setting the core configuration for the Azure Cloud Service package itself, but also some configuration for the Web Roles and/or Worker Roles contained in that package, along with the SSL certificate information.
AppSettings vs. Azure ConfigurationSettings
In most non-Azure ASP.NET web applications, you would specify most of your configuration in the appSettings or connectionStrings elements. For example, you might have something like this in your web.config:
<appSettings> <add key="TestSettingValue1" value="This is the first test value"/> <add key="TestSettingValue2" value="This is the second test value"/> </appSettings>
And then this in your ASP.NET MVC controller (using the handy AppSettingsHelper from the MMDB.Shared library, available on NuGet):
public ActionResult Index() { var model = new SettingsViewModel { TestSettingValue1 = AppSettingsHelper.GetSetting("TestSettingValue1"), TestSettingValue2 = AppSettingsHelper.GetSetting("TestSettingValue2") }; return View(model); }
And this in your Razor view:
<p>TestSettingValue1: @Model.TestSettingValue1</p> <p>TestSettingValue2: @Model.TestSettingValue2</p>
Which ends up looking like this:
While this sort of works in Azure, your web.config gets bundled up inside the Azure package file, so it can’t easily be changed when you promote it from one environment to another. Instead, when you deploy your Azure package to a given environment, you also include a configuration file specific to that environment.
So forgetting the web.config for now, but your Azure configuration file might look like this:
<ServiceConfiguration serviceName="MMDB.AzureSample.Web.Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="2" osVersion="*" schemaVersion="2014-01.2.3"> <Role name="MMDB.AzureSample.Web"> <Instances count="1" /> <ConfigurationSettings> <Setting name="TestSettingValue1" value="Vote Quimby" /> <Setting name="TestSettingValue2" value="He'd vote for you" /> </ConfigurationSettings> <Certificates> <Certificate name="MySSLCert" thumbprint="61475037B69532AA6EE96936DF9CBC463A5F5FE8" thumbprintAlgorithm="sha1" /> </Certificates> </Role> </ServiceConfiguration>
Now you’re ASP.NET MVC controller might look like this:
var settingsAdapter = new MMDB.Azure.Settings.AppSettingsAdapter(); var model = new SettingsViewModel { TestSettingValue1 = settingsAdapter.GetSetting("TestSettingValue1"), TestSettingValue2 = settingsAdapter.GetSetting("TestSettingValue2") }; return View(model);
And that pulls the settings from the Azure configuration file instead:
Note: This code using our MMDB Azure Settings library, also available on NuGet. This library will check to see if you’re actually running in an Azure environment (or even the local Azure emulator), and if so will try to pull the values from the Azure configuration file, and then will fall back and check the web.config appSettings if it’s not found there. Alternatively, it will detect if you’re not running in Azure (either in IIS or just debugging with IIS Express in Visual Studio), in which case it will skip the Azure components and just check the web.config. This has been invaluable for us to be able to get our whole application working fast locally as a normal ASP.NET website, but then giving us the flexibility to include an Azure configuration file when we deploy to production without having to change any code.
Configuring Azure
Now, this all seems simple enough right? You may already have some build or deploy scripts that are doing some sort of XML poke to inject values into your web.config at a given XPath, so why can’t you just do the same thing for the Azure file?
Or, to ask another question with the same answer, what is a major reason people hate XML and have been fleeing to JSON?
The answer is XML namespaces. If you try to write a simple XPath to inject a ConfigurationSetting value, you might try the following:
/ServiceConfiguration/Role[@name="MMDB.AzureSample.Web"]/ConfigurationSettings/Setting[@name="TestSettingValue1"]/@value
And then you would probably be confused why it didn’t find any nodes.
The reason is this xmlns attribute at the top of the configuration file:
<ServiceConfiguration serviceName="MMDB.AzureSample.Web.Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="2" osVersion="*" schemaVersion="2014-01.2.3">
That defines the default namespace to be http://schemas.microsoft.com/blah/blah/blah, and therefore that namespace must be provided at each element when you’re executing an XPath. Sure, there are ways to simplify this in .NET code, like defining a prefix upfront for the namespace and putting that in a NamespaceManager and passing the NamespaceManager through to the SelectNodes call and …zzzzz…
I’m sorry, you seem to have dozed off. What I was getting at is that this is annoying and wasteful and let’s not do it that way.
Sriracha’s Deploy Azure Cloud Service Task to the rescue
As we mentioned in the last post and above, you can use the Sriracha command line deployment tools to easily push an Azure package to up to the cloud.
And since the poking configuration values into Azure configuration files can be painful, we made sure to simplify this process as well.
So if we use the following configuration file to call the Sriracha Azure Deploy Cloud Service Task, it will automatically inject the values into the our Azure configuration file during the deployment:
{ "AzureSubscriptionIdentifier": "ThisIsYourAzureSubscriptionIdentifier", "AzureManagementCertificate" : "ThisIsYourAzureManagementCertificate", "ServiceName" : "AzureViaSriracha", "StorageAccountName" : "srirachademo", "AzurePackagePath": ".\\MMDB.AzureSample.Web.Azure\\bin\\Release\\app.publish\\MMDB.AzureSample.Web.Azure.cspkg", "AzureConfigPath": ".\\MMDB.AzureSample.Web.Azure\\bin\\Release\\app.publish\\ServiceConfiguration.Cloud.cscfg" "RoleList": { "MMDB.AzureSample.Web": { "InstanceCount": 2, "ConfigurationSettingValues": { "TestSettingValue1": "Vote Quimby", "TestSettingValue2": "He'd vote for you" }, "CertificateThumbprints": { "MySSLCert": "ThisIsYourSSLThumbprint" } } } }
The RoleList should match the list of roles in your package, although everything in it is optional so you only have to include the values for the roles you’d like to configure.
Them, you can control the number of instances, the the ConfigurationSettings, and even add the SSL certificate thumbprint if you’re Azure site is configured for using SSL.
Also, until normal XPath poke approaches, it won’t just replace the configuration elements in your Azure configuration file if they are found, it will add any missing elements as well.
One thing you CAN’T control is the VM size. For some reason, that is buried in the Azure service definition file, which then buried inside your Azure package. So if you want to run ExtraSmall instances in your test site but Large instances in production site, you’d have to edit that service definition file before you build the Azure package. We are also considering a nice-to-have feature during the deployment to crack open the Azure package and update the the VM size value, but is much more involved and some consider it a little scary to be modifying the internals of your packages between environments; but anyone who wants to jump in and submit a pull request for that, it would be greatly appreciated.
Anyhow, that gives you an overview of the how and why, but there is a lot more info on the Sriracha2 wiki: https://github.com/mmooney/Sriracha2/wiki/Azure—Deploy-Cloud-Service-Task