What makes IaC work they way it does

What makes IaC work they way it does

I've worked with Infrastructure as Code for many years. All this time I had never really stopped and asked myself: Why does Infrastructure as Code (IaC) work they way it does? It's kind of magical.

Why does this:

resource "azapi_resource" "resource_group" {
  type     = "Microsoft.Resources/resourceGroups@2021-04-01"
  name     = "rg-${var.environment}-${var.location_short}-core"
  location = var.location
}

Create a resource group? And why, if I run this same code 100x times - have I still only got this one resource group? Why do I not have 100 resource groups.

Previously I would've just said if someone asked me this "Well you see, that is because Terraform is idempotent, meaning you can run the same code again and again and expect the same outcome, the outcome you've declared"

Never did I really ask myself "But what does declarative and idempotent really mean anyways?"

If we google idempotent it says: "denoting an element of a set which is unchanged in value when multiplied or otherwise operated on by itself."

Asking Claude then, it says: Idempotent means that doing something once or doing it 100 times provides the same outcome every time. The word comes from Latin where Idem means "same" and potent means power. So we are saying "same power". (At least according to Claude)

And declare is not something I needed to google, it is pretty simple. I declare it to be this way etc etc.. Because I say so! Now that we know this, we can take a look at the Azure Resource Manager (ARM).

ARM is declarative and idempotent

No matter if you are creating a virtual machine with the CLI, Bicep, Terraform or even interacting with the resources in your application using a language such as C# you will be interacting with the ARM REST API using HTTP requests. More specifically in our use case, creating a virtual machine, we are using the HTTP PUT method. Why is it important to know that we are using HTTP PUT?

HTTP PUT

With an HTTP PUT request we the client decide what it should look like on the server once the request has been submitted. What is the request? It's us sending:

  • az vm create
  • terraform apply

The PUT method is idempotent which means that if we run the same PUT action 10 times we should expect to see the same result each time. Exactly how Terraform and Bicep works.

PUT /users/it/david = we tell the server/API to ensure it looks this way once we're done. If I run this again, I will still only have one used called david

When using PUT we are requesting that the target resource state should be created or replaced and in this case replaced/updated with this new information in our PUT requst body. It could be that we changed a value of an existing resource like retention on a log analytics workspace, update the existing one, don't create a new one with the new retention settings...

What happens when you execute your IaC deployment

If we understand what an HTTP PUT method does we can understand that many IaC languages such as Terraform and Azure Bicep is just turning/wrapping some code and sending it to the ARM REST API as an HTTP Put Method.

Note: "Turning/Wrapping code into HTTP PUT" here is an oversimplification. There is a lot more that goes on under the hood but we can't fit everything into one blogpost, but certainly for future ones!

resource "azurerm_resource_group" "rg" {} -> HTTP PUT {resource-id of rg}
Same goes for the Azure CLI actually if we use that for trying to create a virtual machine.

HTTP PUT express that the target resource should be created if it does not exist, or updated with this new information in our request if it already exists.

Old information: tags = {}
New information:

tags = {
	tagName = "tagValue"
}

Since HTTP PUT is idempotent you can run the same request several times and expect the same result. Since we are using PUT, we the client/person executing the deployment decides or declares what it should look like.

So even if it feels like I am imperatively sending requests with the CLI az vm create to create more VMs, ARM will receive this as PUT request and create the virtual machine if it does not exist, or just update the existing one. It will not create a second virtual machine (unless I specify a new VM name, as this will be a new resource ID).

Does ARM only handle HTTP PUT?

Of course not. The ARM API handles GET, POST, PUT, DELETE, LIST etc. If we take a look at another method like POST for example:

What does HTTP POST accomplish?

When using HTTP POST with ARM we are usually performing certain actions. Stopping, starting or deallocating a virtual machine for example uses HTTP POST.

Unlike with HTTP PUT when we do a POST we allow the server (we are the client) to decide what it looks like after we've submitted our request. With PUT, we the client decide what it should look like and it should be idempotent. This means that if you send two POST to the same resource with different requests you could have weird results. If we use POST twice from two different clients to deallocate a VM that is usually not a problem. But if we use the same HTTP method to create or update something we may end up with duplicates.

We will include a body in the message with input values or instructions for the receiving API to handle something, in this case stop a virtual machine for example.

Here we can see POST being used when deallocating: Virtual Machines - Deallocate - REST API (Azure Compute) | Microsoft Learn

POST https://management....

Here we can see PUT being used when creating a new VM: Virtual Machines - Create Or Update - REST API (Azure Compute) | Microsoft Learn

PUT https://management....

Conclusion

It is always interesting asking yourself "How does this thing actually work?". Like I mentioned I learned Infrastructure as Code, then I learned that it works because it is idempotent and declarative.

Many people stop here. But if we ask ourselves, what does idempotent really mean? Then we can go deeper. Today we went a bit deeper than we've done before. Next question could be things like:

  • What is an HTTP request really?
  • Why do they exist?
  • What else happens when we wrap some code in HCL and run terraform apply
  • How does providers play a role in this?

As you can see, there is more we can look into. Perhaps for future posts 😄


Carl Lindberg
☁️ I will help you code your cloud and automate your future... or at least try to! ☕️ Want to buy me a coffee? https://ko-fi.com/lindbergtech Only if you want to, of course
carlzxc71 - Overview
🚀 Microsoft Azure MVP Infrastructure as Code. carlzxc71 has 45 repositories available. Follow their code on GitHub.