Fear the Side Effect
Create atomic infrastructure
Boo! It’s me, the unexplained permanent drift in your Terraform plans, the random resource recreation, your downtime when deploying a new release, or WORSE your supply chain attack.
The Problem
Sadly, not everything can be atomic, but we can do our best by ensuring things happen with as much idempotency as they can have, Terraform already helps with this by preventing drift, but with some resources it can and will be murder-y, our goal here is to prevent that, there are choices we can take that will help with this.
Cloud infrastructure can and will be messy at some point, and that drives the design of tools that manage it, the latest addition to Terraform, action blocks, or the {local|remote}-exec blocks (you should almost never ever use those).
The count meta-argument seems fine right? Wrong, order is not guaranteed to be deterministic (though most of the time it is) but try removing a resource from the list that you’re iterating against and you’ll find it likely that it will try to recreate all resources created by that count (albeit, Terraform is now smarter and if it can it’ll avoid recreating it)
The Solution…? ¯\(ツ)/¯
First, be idiomatic and follow Terraform’s best practices.
Second, I recommend using validation as much as possible, lean towards configuration objects, optional inputs with default values.
Decent variable definition example
variable "persons" {
default = []
description = "A list of persons with their location and personal details"
type = list(object(map({
last_name = optional(string, null)
name = string
office = optional(string, "New York")
})))
validation {
condition = [for person in var.persons : contains(["New York", "California"], person.office)]
error_message = "Person's office should be eithe New York or California."
}
}
resource "human" "jack_sparrow" {
phone_number = twilio_api_accounts_incoming_phone_numbers.jack_sparrow.phone_number
office = try(var.this_is_my_cool_object.office, "Unkown Location") # доверяй, но проверяй
}
Decent loop example
# This won't create anything if the variable isn't defined, that's good!
resource "human_email" "humans" {
for_each = {
for person in var.persons : person.name => person
} # Won't work if someone has the same name, but that can't never happen...
name = "${person.name}@ibm.com"
}
In Summary
Be conscious about your code, create modules that serve an actual purpose, that have opinions, Terraform is already basically an API wrapper with state tracking built-in, creating modules that just wrap a collection of resources and takes every resource option as an input serves no value, be opinionated, bake security standards, make something actually useful to users.
If you want to read more about this, I’d recommend this blog post by Dave Hall.