Building modern software on infrastructure which doesn’t provide enough flexibility can be a pain. Luckily, with the introduction of Azure Resource Manager (ARM), Microsoft reimagined the way resources should be organized and managed.

In older, classic deployment model (also known as Service Manager Deployments), each resource is a single independent unit which must be managed individually. There is no support for creating dependencies between resources, and automating deployment is possible only with PowerShell scripts. This lack of ability to manage and organize resources makes it hard to efficiently manage large-scale projects where each service can consist of multiple resources, services can be scaled horizontally, and there can be multiple deployment environments.

Azure Resource Manager has become available with the release of Azure Preview Portal in early 2015, but with limited availability of services. With achieving general availability status in December 2015, the portal supports the majority of services, although some of the new ones still have preview status.

Classic deployment model is available through Azure Classic Portal, and ARM model is available through Azure Portal. Some classic resources can also be managed through the new portal - for the full list of supported services, please check the official page.

Basic terminology

Before we dive deeper into ARM features, it’s important to understand the terminology.

  • A Resource is a single manageable item available through Azure, for example, a database, load balancer, virtual network, virtual machine, storage account, etc.
  • A Resource Group is a container that holds related resources.
  • A Resource Manager (ARM) template is a JSON file that defines resources to be deployed to a Resource Group.

Benefits of using ARM

  • deploy, manage, and monitor all the resources for your solution as a group
  • repeatedly deploy your solution throughout the development lifecycle
  • manage your infrastructure through declarative templates
  • define the dependencies between resources, so they are deployed in the correct order
  • apply access control to all services in your resource group with Role-Based Access Control
  • logically organize resources with tags
  • view resource costs per resource group or tag

ARM Template

The ARM template provides a way to create standardized and repeatable deployment process. Multiple resources can be deployed together in the correct order. Resources can access configuration of other resources using resource functions, and automatically configure themselves. Updating existing resource group is done simply by re-deploying the template to Azure.

This is a huge deal for large-scale projects with complex resource dependencies and multiple deployment environments; this ensures that all environments are configured and set up in the same way.

ARM template has following structure:

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": { },
    "variables": { },
    "resources": [ ],
    "outputs": { }
}
  • contentVersion - version of the template
  • parameters - values provided when deployment is executed
  • variables - values used as JSON fragments to simplify template language expressions
  • resources - various resources deployed or updated in a resource group
  • outputs - values returned after deployment

Writing ARM templates is not a trivial task, especially when creating complex environments, but ARM templates contributed by the community may help you jump-start your project.

To develop an ARM template in Visual Studio, you’ll need to install Visual Studio Tools for Azure.


How does it work in practice?

Let’s create a simple ARM template in Visual Studio 2015, step by step.

First, we’ll create a new project - ARMDemo:

Create Resource Manager Solution

On the next screen, you can choose from predefined templates, but we’ll start with the blank one:

Resource Manager Blank Template

You’ll notice that Visual Studio 2015 created three files:

  • azuredeploy.json - template file
  • azuredeploy.parameters.json - template parameters file
  • Deploy-AzureResourceGroup.ps1 - default PowerShell script which takes care of executing deployment. Usually, there is no need to alter it unless you need to customize deployment process.

Open ARM template file ARM template JSON Outline

For now, let’s focus on azuredeploy.json file. The file will be identical to what we already covered in the ARM template section. JSON outline sidebar will also appear to help us visualize the template and navigate through parameters, variables, resources, and outputs. To add a resource, click the button with the plus sign.

To keep it simple, we’ll add only two resources: Web App and SQL database. Some resources may have prerequisites - in that case, Visual Studio will let us choose between existing resource, and creating a new one. For this project, Web App requires a Server Farm, and SQL database requires an SQL server, so we’ll end up with four resources:

  • Web App: arm-demo-app
  • Server Farm: arm-demo-farm (required for Web App)
  • SQL database: arm-demo-database
  • SQL server: arm-demo-sql-server (required for SQL database)

As we add resources, Visual Studio will generate the template. If a resource has parameters (tiers for example), that will also be included in the parameters section of the file, so we can choose which tier of resource to deploy.

The final result is this:

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "arm-demo-farmName": {
            "type": "string",
            "minLength": 1
        },
        "arm-demo-farmSKU": {
            "type": "string",
            "allowedValues": [
                "Free",
                "Shared",
                "Basic",
                "Standard"
            ],
            "defaultValue": "Free"
        },
        "arm-demo-farmWorkerSize": {
            "type": "string",
            "allowedValues": [
                "0",
                "1",
                "2"
            ],
            "defaultValue": "0"
        },
        "arm-demo-sql-serverAdminLogin": {
            "type": "string",
            "minLength": 1
        },
        "arm-demo-sql-serverAdminLoginPassword": {
            "type": "securestring"
        },
        "arm-demo-databaseName": {
            "type": "string",
            "minLength": 1
        },
        "arm-demo-databaseCollation": {
            "type": "string",
            "minLength": 1,
            "defaultValue": "SQL_Latin1_General_CP1_CI_AS"
        },
        "arm-demo-databaseEdition": {
            "type": "string",
            "defaultValue": "Basic",
            "allowedValues": [
                "Basic",
                "Standard",
                "Premium"
            ]
        },
        "arm-demo-databaseRequestedServiceObjectiveName": {
            "type": "string",
            "defaultValue": "Basic",
            "allowedValues": [
                "Basic",
                "S0",
                "S1",
                "S2",
                "P1",
                "P2",
                "P3"
            ],
            "metadata": {
                "description": "Describes the performance level for Edition"
            }
        }
    },
    "variables": {
        "arm-demo-appName": "[concat('arm-demo-app', uniqueString(resourceGroup().id))]",
        "arm-demo-sql-serverName": "[concat('arm-demo-sql-server', uniqueString(resourceGroup().id))]"
    },
    "resources": [
        {
            "name": "[parameters('arm-demo-farmName')]",
            "type": "Microsoft.Web/serverfarms",
            "location": "[resourceGroup().location]",
            "apiVersion": "2014-06-01",
            "dependsOn": [ ],
            "tags": {
                "displayName": "arm-demo-farm"
            },
            "properties": {
                "name": "[parameters('arm-demo-farmName')]",
                "sku": "[parameters('arm-demo-farmSKU')]",
                "workerSize": "[parameters('arm-demo-farmWorkerSize')]",
                "numberOfWorkers": 1
            }
        },
        {
            "name": "[variables('arm-demo-appName')]",
            "type": "Microsoft.Web/sites",
            "location": "[resourceGroup().location]",
            "apiVersion": "2015-08-01",
            "dependsOn": [
                "[resourceId('Microsoft.Web/serverfarms', parameters('arm-demo-farmName'))]"
            ],
            "tags": {
                "[concat('hidden-related:', resourceId('Microsoft.Web/serverfarms', parameters('arm-demo-farmName')))]": "Resource",
                "displayName": "arm-demo-app"
            },
            "properties": {
                "name": "[variables('arm-demo-appName')]",
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('arm-demo-farmName'))]"
            }
        },
        {
            "name": "[variables('arm-demo-sql-serverName')]",
            "type": "Microsoft.Sql/servers",
            "location": "[resourceGroup().location]",
            "apiVersion": "2014-04-01-preview",
            "dependsOn": [ ],
            "tags": {
                "displayName": "arm-demo-sql-server"
            },
            "properties": {
                "administratorLogin": "[parameters('arm-demo-sql-serverAdminLogin')]",
                "administratorLoginPassword": "[parameters('arm-demo-sql-serverAdminLoginPassword')]"
            },
            "resources": [
                {
                    "name": "AllowAllWindowsAzureIps",
                    "type": "firewallrules",
                    "location": "[resourceGroup().location]",
                    "apiVersion": "2014-04-01-preview",
                    "dependsOn": [
                        "[resourceId('Microsoft.Sql/servers', variables('arm-demo-sql-serverName'))]"
                    ],
                    "properties": {
                        "startIpAddress": "0.0.0.0",
                        "endIpAddress": "0.0.0.0"
                    }
                },
                {
                    "name": "[parameters('arm-demo-databaseName')]",
                    "type": "databases",
                    "location": "[resourceGroup().location]",
                    "apiVersion": "2014-04-01-preview",
                    "dependsOn": [
                        "[resourceId('Microsoft.Sql/servers', variables('arm-demo-sql-serverName'))]"
                    ],
                    "tags": {
                        "displayName": "arm-demo-database"
                    },
                    "properties": {
                        "collation": "[parameters('arm-demo-databaseCollation')]",
                        "edition": "[parameters('arm-demo-databaseEdition')]",
                        "maxSizeBytes": "1073741824",
                        "requestedServiceObjectiveName": "[parameters('arm-demo-databaseRequestedServiceObjectiveName')]"
                    }
                }
            ]
        }
    ],
    "outputs": {
    }
}

The JSON outline sidebar helps to quickly jump between JSON nodes:

JSON outline sidebar for ARM template

To deploy the template to Azure, right-click the ARMDemo project in the solution, choose Deploy, and then New.

ARM template deployment dialog

Now we need to create a new resource group and choose a location. We’ll name it ARMDemo, and edit its parameters.

Note: SQL server requires a strong password or the deployment will fail.

ARM template deployment parameters

The deployment will complete in a couple of minutes, and we’re ready to explore what has been created on Azure Portal in the ARMDemo resource group.

Resource group from ARM template

This way, the deployment process can be repeated to update existing resource group, or to create multiple new resource groups.

More articles

Your opinion matters!

comments powered by Disqus