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).
Try to find the most deterministic way to do things, you will be grateful later.
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#
resource "human_email" "humans" {
for_each = {
for person in var.persons : person.name => person
}
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.