The Immutable Infrastructure pattern declares that you should never change the state or configuration of an infrastructure resource once it’s been deployed: you replace it entirely with new resources with the new configuration.

This pattern originated with VMs and it’s now standard for containers; this is how Kubernetes and other container orchestrators do rolling updates, for instance.

The point of Immutable Infrastructure is that you always know what state your infrastructure is in, and you can trivially switch back to the previous, known-good resources because they’re still there. This implies the use of some kind of proxy or balancer in front of your resources.

You can, and should, use this pattern with cloud resources as well. With Azure it requires a bit of planning due to the way ARM deployments work, and the fact that Azure resources can be children of other resources.

The Circle of Life Link to heading

You’re humming the theme from The Lion King right now, aren’t you. It’s okay, I won’t judge.

Azure recommends that you use resource groups to group resources that share a lifecycle - that is, they are always created and destroyed together. So far, so good: you can apply the Immutable Infrastructure pattern to this. Instantiate a new resource group containing the new resource(s), perform whatever load-balancing switchery is necessary, delete the old resource group. The ARM APIs’ deployment scope requirements nudge you in this direction anyway; the smallest scope you can deploy to is the resource group, so you’re going to be thinking in terms of groups anyway.

Sharper Than A Serpent’s Tooth Link to heading

Where this gets complicated is with Azure resources that are children of other resources, or that have existential dependencies on other Azure resources. A typical example of this is an App Service Environment: an App Service Environment can contain many App Service Plans, and each App Service Plan can contain many App Services. A pure Immutable Infrastructure design would look something like this:

Immutable Infrastructure ASE

There are two big issues with this model:

1. The Life Cycles Aren’t Independent Link to heading

Because these resources exist in a parent-child hierarchy, you can’t replace any of the tiers without also replacing all of its children. That can be onerous if all you need to do is make a change to an App Service Plan like adjusting a SKU or an autoscaling rule. If you’re cleaving to pure Immutable Infrastructure, you need to replace all its App Services and redeploy code to them all as well.

2. One Resource Per Resource Group Link to heading

Technically, any of the resources could need to be replaced independently of any other. You might need to twiddle the TLS support on a single App Service, or change the autoscaling parameters on an App Service Plan. That means you end up with as many resource groups as you have resources, which leads to resource group proliferation and cognitive complexity.

Is This Actually A Problem? Link to heading

I’d say that really depends on your level of Infrastructure-as-Code DevOps maturity. If you already have robust Bicep templates for defining resources, and reliable infrastructure deployment pipelines, then you should be able to implement very fine-grained Immutable Infrastructure even with this kind of parent-child dependency. Infrastructure deployment and code deployment should be tightly integrated so that you can deploy a new App Service Plan and all its App Services, deploy code to the App Services, and control whatever load balancer sits in front of the whole set.

If you’re not there yet, you can still start with the low-hanging fruit. In this model that’s the App Services. Implement Immutable Infrastructure for them so you’re not dealing with the parent-child dependencies. Group related App Services into a single resource group even if they don’t technically have identical lifecycles so you can tackle the challenge of deploying infrastructure and code together. Once you’re comfortable replacing entire sets of Azure Resources programmatically and without errors, you can extend down to the App Service Plan level.

Astute readers will note that App Service deployment slots are effectively Immutable Infrastructure for App Services. I’ve used App Service Environments/App Service Plans/App Services as an example here because they’re the most common Azure Resources with parent-child relationships. You really should be using deployment slots rather than trying to implement Immutable Infrastructure at the App Service level. Don’t Reinvent The Square Wheel.

A Final Caveat Link to heading

Immutable Infrastructure is designed for, and only works for, stateless resources. Handling resources that contain persistent data is outside its scope. This is obvious for databases like Azure SQL and CosmosDB but don’t forget that Key Vaults are also stateful databases.