Infrastructure as Code in Azure

A guide to get started with Infrastructure as code tools in Azure.

Raywon Kari - Published on July 14, 2020 - 10 min read



Introduction


Infrastructure as Code (IaC), is a practice which enables the use of automation to manage resources declaratively such as provisioning, configuring, updating, de-commissioning etc. More theory around this topic can be found on wikipedia. IaC practises have gained a lot of popularity because it brings lot of consistency, predictability, reusability into the system.

Since this is the cloud era at the moment, cloud providers more or less have given the same ability to use IaC practises to manage cloud infrastructure, and IaC in Azure is powered by a service called Resource Manager. Using it, we declaratively define cloud resources in JSON 📃 templates and deploy them to Azure. Rest is taken care by the service itself.

In this blog post, we will explore the basics of Azure Resource Manager (ARM), how to use it by taking an example and some tips & tricks.



Basics of Azure


It is important to understand how Azure works and the scope of it. Azure provides four levels of scope:

  • Management Groups
  • Subscriptions
  • Resource Groups
  • Resources

We can apply settings at any of these levels, and the level we select, defines how widely those settings are applied. Also lower levels inherit settings from higher levels such as policies etc.

If we try to understand from the lower scopes, all cloud resources are grouped into resource groups. Resource groups are part of subscriptions, and subscriptions are organised and managed by management groups. If we configure any policies or settings in management groups, those are enforced all the way down to the resource level.

Following diagram illustrates how Azure organises and manages resources.

azure



Basics of Azure Resource Manager


When we go out to a restaurant, what do we usually do 🍛 ?

We tell what we want to eat, we get the food 😄 and at the end we pay the bill.

That is exactly what is happening with Resource Manager as well. We tell what we want in the templates, we upload the template, and it creates and configures the stuff for us. So writing the template is ordering the food, creating and configuring the resources is getting the food, and paying the bill here is the common aspect 😉.

Resource manager is primarily used to manage azure resources. Whenever we send a request to resource manager either using CLI, or APIs, it authenticates and authorizes the request, resource manager thereafter sends the request to Azure, which takes the request and handles it. Since all the user requests are handled by the same API, resource manager comes with a consistent behavior.

Resource manager is powered by JSON templates 📃. In the templates, one or more resources are defined declaratively to deploy into resource groups, subscriptions, management groups or tenants. Once we have a template, we simply submit it to Resource manager, and it performs operations specified declaratively in the template.

Following diagram is an example of how Resource Manager works.

arm



Playing with ARM Templates


A Template is the heart of ARM service, therefore it is important to understand the format and how to write a template. A template can consist of the following sections:

  • Parameters => In here, we specify values to variables which are used during deployment time, which allows the same template to be re-used in different environments.
  • Variables => In here, we parameterise variables, making the templates re-usable, instead of hard coding values. These can be constructed from parameter values.
  • User-defined functions => In here, we can write custom functions which has a logic, depending on our needs, which can make the templates more simple.
  • Resources => In here, we define what azure resources we want to create as part of that particular template.
  • Outputs => In here, we define what values from resources are to be exposed.

Here is an example format of the template in JSON:

{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
...
},
"variables": {
...
},
"resources": [
...
],
"outputs": {
...
}
}

Detailed documentation on templates can be found here.

In this blog post, we will create a webapp, and deploy a default .net website to it, using ARM templates. By the end of it, we will see how to use templates to spin up resources.

First of, we create a JSON file named deploy.json. We will need a schema, contentVersion, parameters, variables, resources sections in it.

In the parameters section, we will define the name of the webapp, location, sku, the git repo to deploy. In the resources section, we will define the web app plan and the web app.

Here is how the parameters section will look like:

"webAppName": {
"type": "string",
"defaultValue": "raywon-web-app",
"metadata": {
"description": "Web app name."
},
"minLength": 2
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"sku": {
"type": "string",
"defaultValue": "F1",
"metadata": {
"description": "The SKU of App Service Plan."
}
},
"repoUrl": {
"type": "string",
"defaultValue": "https://github.com/Azure-Samples/app-service-web-dotnet-get-started",
"metadata": {
"description": "Optional Git Repo URL, if empty a 'hello world' app will be deploy from the Azure-Samples repo"
}
}

Here is how the resources section will look like:

{
"apiVersion": "2019-08-01",
"type": "Microsoft.Web/serverfarms",
"name": "[variables('appServicePlanPortalName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('sku')]"
}
},
{
"apiVersion": "2019-08-01",
"type": "Microsoft.Web/sites",
"name": "[parameters('webAppName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanPortalName'))]"
],
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanPortalName'))]"
},
"resources": [
{
"type": "sourcecontrols",
"apiVersion": "2019-08-01",
"name": "web",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('webAppName'))]"
],
"properties": {
"repoUrl": "[parameters('repoUrl')]",
"branch": "master",
"isManualIntegration": true
}
}
]
}

One common thing we can observe in both these sections is how we are referencing the values of paramters and variables to fetch the actual values. For example:

parameters('location')
# This will fetch the location value from parameters section
variables('appServicePlanPortalName')
# This will fetch the appServicePlanPortalName value from the variables section

Full template can be found here.

Now that we have our template ready, lets try to upload the template to Azure first. We will use the Azure CLI. For instructions on how to install Azure CLI, Refer here

AZ Login:

In order to use azure CLI, we need to first auth ourselves with azure using the az login command, which will open a browser, we then need to login to our azure account. Once that is done successfully, our CLI session is then ready to use against azure.

Simply run in your terminal:

# This command will not work if
# you have MFA enabled.
az login
# If you have enabled MFA,
# fetch your tenant ID and
# Use the following command
az login --tenant TENANT_ID

Tenant ID can be usually found in the Active Directory service in Azure here.

Once az login is done, we should see a response like this:

You have logged in. Now let us find all the subscriptions to which you have access...
[
{
"cloudName": "AzureCloud",
"homeTenantId": "XXXXXXXX",
"id": "XXXXXXXXX",
"isDefault": true,
"managedByTenants": [],
"name": "Raywon's Azure",
"state": "Enabled",
"tenantId": "XXXXXXXX",
"user": {
"name": "its@raywonkari.com",
"type": "user"
}
}
]
Resource Group:

Before creating any azure resources, we need a resource group i.e., kind of a parent container, where the resources will reside.

Lets create a resource group using the following command:

az group create \
--name rg-raywon-web-app
--location "North Europe"
# We should get a JSON response as follows
{
"id": "/subscriptions/xxxx/resourceGroups/rg-raywon-web-app",
"location": "northeurope",
"managedBy": null,
"name": "rg-raywon-web-app",
"properties": {
"provisioningState": "Succeeded"
},
"tags": null,
"type": "Microsoft.Resources/resourceGroups"
}

As we can see from the provisioning state, the resource group creation has succeeded.

Deploy:

Now lets try to deploy our resources using the template.

TEMPLATE_FILE="./deploy.json"
az deployment group create \
--name raywon-web-app-template \
--resource-group rg-raywon-web-app \
--template $TEMPLATE_FILE
# This command will take a minute

Once the command exits, we will get a big JSON response, and if we search for privisioning state, we should be able to see if the deployment has succeeded or not.

Head over to the Azure console, and navigate to App Services page, usually located here and try to open the website.

Mine is available here: https://raywon-web-app.azurewebsites.net

webapp

Similar workflow can be used to update the template and deploy it as well. This example is the basic version of using resource manager.

Next Steps:

We could explore:

  • Uploading a template into azure and deploy from console directly. Templates can be uploaded manually here.
  • Adding new resources to an existing template, and deploying them using CLI.
  • Setting up an end-to-end flow using Git, ARM templates, Azure Devops etc, and also setting up deployment verifications, health checks, redeployment, template re-usability across different environments etc.
Summary:
  • We have seen the basics of Azure and Resource manager.
  • Working with ARM templates.
  • Deploying a template.


Tips & Tricks


This will be last section of this blog post, and in here we will explore some of the tips and tricks when using CFN.

Limits:
  • Watch out for the template limits such as file size, number of resources variables per template etc , which can be found here.
Linked ARM Templates:
  • Split out big projects into smaller workloads. Make sure to split out resources into its own templates, and link one another.
Default parameter values:
  • Assign default values to parameters so users always have a valid working values.
  • For example, VM size. Assign a default value, and users will customise it if needed.
Security:
  • Use secretstring, if you want to enter sensitive values such as passwords.
  • We can store templates in storage accounts, and access to them can be given using SAS tokens. Templates can be uploaded using Set-AzStorageBlobContent in PowerShell or az storage blob upload commands in linux based terminal.
Toolkit:
  • Use ARM template toolkit to check if your templates are following best practises. It is a simple script.
  • Toolkit can be found here.

You can find all the code used in this blog post here.

If you have any questions/thoughts/feedback, feel free to contact me in any platform you prefer.



Raywon's Blog © 2020 - 2021

Built with Gatsby & React