diff --git a/content/docs/posts/index_oops b/content/docs/posts/index_oops new file mode 100644 index 0000000..1e1b7c6 --- /dev/null +++ b/content/docs/posts/index_oops @@ -0,0 +1,82 @@ +--- +title: "Index out of range got me" +date: "2026-03-26" +tags: + - terraform + - oops +categories: + - iac +--- + +Accidently nuked half of some resources and broke DNS (yes, it is in fact always DNS). One of the first things I learned and is on a lot of guides for terraform is how `count` works. It's one of the meta-arguments you can use with most resources, others are + +``` +depends_on +count +for_each +provider / providers +lifecycle +``` + +Here's an example for count before I show my oops. + +```hcl +variable "names" { + default = ["alice", "bob", "carol"] +} + +resource "aws_instance" "web" { + count = length(var.names) + ami = "ami-0c55b159cbfafe1f0" + instance_type = "t3.micro" + + tags = { + Name = var.names[count.index] + } +} +``` + +Remove `"bob"` → `default = ["alice", "carol"]`. You'll see this in your terraform run of web[1] transitioning. + +``` + ~ Name = "bob" -> "carol" +``` + + +carol shifts from index 2 → 1, so Terraform modifies the bob instance to become carol, and destroys the old carol. Two changes instead of one. + +`count` is very quick and easy to use but honestly I avoided it. If I read of a feature that has to be used with extra considerations, I'd rather use the pattern that doesn't let me screw up if my coffee has fully kicked in. + +--- + +In my case, I added to middle of an array in a variable but the implementation logic was a count instead of a `for_each`. + +Here's an implementation that guarentess uniqness in the array to avoid collisions and doesn't care about order. + + +```hcl +# Before (vulnerable — index shuffle on any list change) +resource "aws_instance" "web" { + count = length(var.names) + ami = "ami-0c55b159cbfafe1f0" + instance_type = "t3.micro" + + tags = { + Name = var.names[count.index] + } +} + +# After (safe — each instance is an independent resource) +resource "aws_instance" "web" { + for_each = toset(var.instances) + ami = "ami-0c55b159cbfafe1f0" + instance_type = "t3.micro" + + tags = { + Name = each.key + } +} +``` + +Even better is that with the first method you access the resource like `aws_instance.web[0]` but with the 2nd you get a much more descriptive and assuring `aws_instance.web["bob"]` +