To ARM, or not to ARM, that is the question
Tags: azure, arm, automation, infrastructureascode
Published: 2019-06-13 18:51:02

Over the last few years I have got this same question *maaaany* times. 
It's all about how do choose your Infrastructure-as-Code (IaC) solution when you're working with Azure. These questions come in different shapes and forms but many of them can be summarized into this simple question:

"*Should I use __\<placeholder\>__ instead of ARM templates?*"

Of course you can replace __\<placeholder\>__ with different options but most commonly offered ones are Terraform and Azure CLI based solutions.

Typically people expect that I give them one simple answer like "*do this or do that*" but I don't think this is one of those questions that can be answered like that. 

Let's start from the existing experience within your development team. Is there already people that know one of these options better than others? Is it single person or are there even more people with similar expertise? Do you plan to reuse single applications IaC assets to different applications or are you actually purchasing these different applications from different vendors and they bring their expertise always to the table and you don't want or can't be too much involved with that? Etc.

Above expertise and experience related questions can already mean that you decide to re-use existing knowledge because that enables you to move faster. I actually compare this often to programming language selection in software development: If you know e.g. Node.js well then why would you use e.g. Python in your next app if there is nothing pushing you to that direction (and of course you can swap these programming languages with any programming languages in this example).

If you don't have expertise or you are looking to evaluate these different options then I highly recommend you to try out ARM templates. There are couple of reasons which you should know before choosing one or another. 

Let's first compare three different simple deployments for (as simple resource as) Application Insights and see how the resource groups look after the deployment.

Azure CLI method ([example] (https://docs.microsoft.com/en-us/cli/azure/ext/application-insights/monitor/app-insights/component?view=azure-cli-latest#ext-application-insights-az-monitor-app-insights-component-create)):

![Azure CLI](https://thepracticaldev.s3.amazonaws.com/i/3uavybvpfs3hrm46z6u8.png)

Terraform method ([example](https://www.terraform.io/docs/providers/azurerm/r/application_insights.html)):

![Terraform](https://thepracticaldev.s3.amazonaws.com/i/bpkzbd7ppzdka96i7zou.png)

ARM template method ([example](https://gist.github.com/JanneMattila/2293534f4f8d5e2d0303bec8e353fc21)):

![ARM template](https://thepracticaldev.s3.amazonaws.com/i/osu8a4ui7kczrxc8kq3c.png)

Okay so the end result looks the same. Or does it? Can you spot the difference? If you take a closer look at the ARM template deployment screenshot you should notice `Deployments: 1 Succeeded` (top right). This is important difference between these different deployment methods. ARM template deployments update the resource group about the deployment and thus it's capable of showing history of these deployments and details of each deployment. Read more about template deployment from [here](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview#template-deployment).

This might look like a small thing but if you then combine it with deployments from Azure DevOps you have now easy way to map these two together. 

See "Dev" environment deployments for our demo application called "app" in Azure Portal (you can derive "Dev" directly from resource group naming *serviceshortname-environment-rg*):
![Azure Portal deployments](https://thepracticaldev.s3.amazonaws.com/i/fz55n3cjlvu3bte2bf1h.png)

See "Dev" environment deployment in Azure DevOps side:
![Azure DevOps Releases](https://thepracticaldev.s3.amazonaws.com/i/hz2mvnveq4jwm45mhdq8.png)

And as you can see they map directly one-to-one.

This becomes extremely handy when *PIIP* hits the fan and you're troubleshooting this from Azure side and you need to quickly identify the actual deployment that has been done for the application. With this solution it's trivial to see what is the last deployment done and when for production environment:

![Production deployment history](https://thepracticaldev.s3.amazonaws.com/i/w4vthp38b8if835mdg3m.png)

This enables you to quickly jump into Azure DevOps side and see the content of the release and difference between previous and current release from source control and work item perspectives.

---------------------------

If you still decide to use Azure CLI script based deployment then they typically follow this pattern:
- *add resource x*
- *add resource y*
- *add resource z*

etc.

Have you thought what happens if you decide to get rid of *resource y*? You have to get rid of it manually since the script doesn't magically remove it (unless you have code that does that). And you should not even have access rights to production to do removal (and not to even think how scary and risky that operation would be!). Terraform and ARM template deployments do manage this situation correctly. Just remember to use `-Mode Complete` in your ARM template deployment for the [deployment mode](https://docs.microsoft.com/en-us/azure/azure-resource-manager/deployment-modes):

```powershell
$result = New-AzResourceGroupDeployment `
    -DeploymentName $deploymentName `
    -ResourceGroupName $ResourceGroupName `
    -TemplateFile $Template `
    @additionalParameters `
    -Mode Complete -Force `
    -Verbose
```

This means that resources that are not anymore part of your template will be removed automatically from the target resource group.

---------------------------

One other comment I hear a lot is that "*ARM templates are static*" and that "*there is no place for custom logic in them*". I agree and I think it's a good thing. I like my ARM templates nicely controlled via parameters but otherwise they are just plain static files. But that's also the reason why I always bundle PowerShell `deploy.ps1` file next to them (and here you can swap PowerShell to e.g. Bash). I think this makes good combination between these two: PowerShell is the entrypoint for the deployment and actual infrastructure assets are described in the actual ARM templates. This also enables very nice development experience. Imagine that you get new developer to the team and they want to spin up your infrastructure to their sandbox subscription. This is what they have to do:
```powershell
Login-AzAccount
Select-AzSubscription -SubscriptionName YourSubName
cd C:\YourPathToApp\deploy
.\deploy.ps1
```

Isn't that crazy easy? It's easy because `deploy.ps1` has meaningful default values for the deployment. See example of this kind of setup from [here](https://github.com/JanneMattila/118-FrontDoorApp/blob/master/deploy/deploy.ps1#L1-L21). 

Now developer doesn't have to know/remember how to set up the parameters correctly to the `New-AzResourceGroupDeployment` because all these are handled inside the `deploy.ps1` file. And of course all the parameters that needs to be overridden per environment can be overridden by providing parameter value to it.

Above also means that you can use this same method in your deployment pipelines. So instead of this:
![Resource group deployment task](https://thepracticaldev.s3.amazonaws.com/i/86u4y3q8vc33utmqemhd.png)
(screenshot is clipped since it's soooo long!)

You can just use this:
![deployment using deploy.ps1](https://thepracticaldev.s3.amazonaws.com/i/sb9e7a9w3i5hn8jrnl98.png)

---------------------------

I hope that I managed to demonstrate that you should give ARM a spin if you haven't previously tried it. Or at least you now should know why I like ARM. To summarize:

> Do not underestimate the power of ~~Dark side~~ ARM templates!