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):
Terraform method (example):
ARM template method (example):
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.
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.
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:
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
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:
$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:
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.
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.
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 sideARM templates!