Infrastructure as code with Azure
From the moment a developer writes the code for a new feature or to resolve a bug, it's the start of a journey. Hopefully, this journey will begin with a form of a commit towards a version control system. The destination of this journey is known: the product environment (whatever that may be in the relevant context). The journey can be short and simple or long and complicated, and the duration will mainly be determined by the quality assurance process and the level of automation of the CI/CD pipeline.
Build and release
These days it is pretty common for teams to have their builds automated and triggered on a new commit. In this first CI step, it is also pretty common to run some form of (unit)testing. The next step is the release process, in most cases, this process will take a build and deploys it to an environment whether it is a qa environment, a ring, canary or straight to production.
But where is it releasing to? It used to be a metal box but these days it is more likely a virtual machine, container, or some hip cloud service. Because the deployment targets used to be more static and stateful, their creation is often not automated. If you cannot recreate your environment with its configuration with up-to-date documentation or automation you are suffering from technical debt that you should not underestimate!
Keeping documentation perfectly up to date
Based on empirical evidence, there will be a remote possibility that this will happen. And if it would, it is still extremely inefficient.
// TODO: write some documentation
// TODO: Remove this code after 2009-01-01
Infrastructure as code
Creating your resources automatically will pay off really quickly. Let’s say you dream up some new piece of code that uses some storage service and web service in Azure, it is only a few clicks to create these in the Azure Portal. But you will probably do this in a safe development environment where you have all the access you can dream of. Your two new services and their configuration will also be deployed in one or more test environments and the production environment. Scripting and parameterizing the creation of these resources them will make you more efficient fast. An extra benefit is that your scripts can be reviewed and that issues with for example naming convention can be spotted early on. If you treat your scripts as code and store them in a version control system you have a perfect historical record of how a resource was created.
Great, let’s do this! Tell me how!
Like always, there is more than one way to accomplish this. Azure exposes apis to manipulate resources, there are a lot of tools that can talk to these apis. For now, let’s focus on the options closest to Azure itself that can fairly easily be triggered in an Azure Pipeline.
The following three options contain an example that is taken from the official Azure Documentation pages. These examples show the creation of an Azure Web App with a Service Plan.
1. ARM Templates
Maybe the most common way to do ‘Infrastructure As Code’ in Azure is to use ARM Templates. ARM stands for Azure Resource Manager and has been the standard set of apis for some years now, the services that are referred to as ‘Classic’ in Azure are controlled with the ASM (Azure Service Management) api.
ARM Templates are a JSON representation of one or more resources in Azure. In a single template, you can define a complex set of resources and their dependencies within a resource group. If you are in the Azure portal you can go to any resource and export an ARM template, this sounds like the way to start, doesn’t it? Even though it sounds like a perfect and simple solution this will generate a complex script that is a lot better readable by a machine than by a human. If you look at the example below it might be readable enough, but for me, something like this is hard to write from scratch without too much experience.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"webAppName": {
"type": "string",
"metadata": {
"description": "Base name of the resource such as web app name and app service plan"
},
"minLength": 2
},
"sku":{
"type": "string",
"defaultValue" : "S1",
"metadata": {
"description": "The SKU of App Service Plan, by default is Standard S1"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources"
}
}
},
"variables": {
"webAppPortalName": "[concat(parameters('webAppName'), '-webapp')]",
"appServicePlanName": "[concat('AppServicePlan-', parameters('webAppName'))]"
},
"resources": [
{
"apiVersion": "2018-02-01",
"type": "Microsoft.Web/serverfarms",
"kind": "app",
"name": "[variables('appServicePlanName')]",
"location": "[parameters('location')]",
"properties": {},
"dependsOn": [],
"sku": {
"name": "[parameters('sku')]"
}
},
{
"apiVersion": "2018-11-01",
"type": "Microsoft.Web/sites",
"kind": "app",
"name": "[variables('webAppPortalName')]",
"location": "[parameters('location')]",
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
]
}
]
}
2. Azure CLI scripts
Command line tools everywhere have gained popularity in recent years, so has the Azure CLI. The Azure CLI is basically a command line version of the Azure Portal with good feature discoverability due to the inline help functionality. Because you can script the use of a command line tool in PowerShell or bash you can create parameterized automation scripts that you can store in source control and run in CD pipelines. The flow of creating resources will be similar to the steps you would normally take to create something in the portal.
az group create -l westeurope -n MyResourceGroup
az appservice plan create -g MyResourceGroup -n MyPlan -l westeurope
az webapp create -g MyResourceGroup -p MyPlan -n MyUniqueAppName
3. Azure Management Libraries for .NET
Normally you would see this library inside the parts of your application that need to control infrastructure in Azure. For example, if you have a multi-tenant SAAS application that has SQL databases for every tenant you can use the libraries to create a new db on a new customer signup. Maybe you have some really specific scaling requirements that you can automate from within your application. But if the developers are comfortable writing C# all day this might also be a fine way to script/code the creation of resources. With the Azure Management Libraries, you can define services using a fluent api.
IAzure azure = Azure.Authenticate(credFile).WithDefaultSubscription();
var webApp = azure.WebApps.Define(appName)
.WithRegion(Region.USWest)
.WithNewResourceGroup(rgName)
.WithNewFreeAppServicePlan()
.Create();
Making a choice
The most important part is embracing the fact that it is important to commit the definition of the infrastructure your code needs to run into a source control system. It still seems like the official recommendation is to use ARM templates. The strong part of the ARM templates is that you do not define a script but you define a state that you want your resources in, this means that you have reproducible behaviour no matter the state you start with. The Azure CLI commands are simple to write from scratch, but you are not defining a complete state like with ARM templates so you might need to script out a bit of flow. Most of the commands are idempotent and will not throw errors on recreation of a resource, but I found out the hard way that some of the commands still have some unexpected behaviour. Using the Azure Management Libraries provides maximum control in complex setups and they certainly have their place but you are building yet another application instead of just a script.
Infrastructuur as code with Azure
Infrastructuur as code with Azure