iac.htora.dev · security templates

Home/Start here/Common mistakes

Common mistakes

The same handful of things go wrong the first time with each tool. None of them are subtle once you have seen them.

Terraform

Typing yes without reading the plan

The plan is the only place Terraform tells you it is about to destroy something. Skipping it is how a database goes away during what was meant to be a tag change. What to look for in a plan.

Committing the state file

terraform.tfstate holds every value Terraform handled, in plain text, passwords included. It belongs in a bucket with encryption and access control. Put it in .gitignore on the first commit, because taking it out of history later is a much longer afternoon.

Keeping state on a laptop for shared infrastructure

It works until the second person runs apply. Then there are two records of reality, and Terraform starts offering to create things that already exist.

Pinning nothing

Without a version constraint on the provider, a run next month can behave differently from a run today with no commit in between. Every template here sets a constraint on both Terraform and the provider. The exact versions are recorded in .terraform.lock.hcl the first time you run init, and that file belongs in Git. It is the actual pin.

Using destroy to tidy up

terraform destroy removes everything that folder manages, including the things you were not thinking about. To remove one resource, delete it from the code and apply.

Ansible

Reaching for command or shell

A raw command cannot tell whether the work is already done, so it does it again every run and reports changed every time. Once that number is never zero, nobody reads it, and a real change hides in the noise. Use the module for the job, or add changed_when so the report stays honest.

Restarting the service you are connected through

Restarting sshd with a broken config ends your session and every other one. Reload instead. A reload leaves existing connections alive, so you still have a way in to fix it.

Skipping check mode

--check --diff shows what would change and the exact lines it would write. It is the closest thing Ansible has to a plan, and it costs one extra run.

Secrets sitting in group_vars

Variables files end up in Git like anything else. Use ansible-vault, or pull the value from a secrets store at run time.

Believing ok means it took effect

A task can write a file perfectly and still change nothing, if the service never reads that file. The SSH template runs sshd -T afterwards and fails if the setting did not land. That is the general shape of the fix: read back the result after the write.

Argo CD

Adding applications through the web interface

It works, and it puts the list of what should be running in the one place that is not version controlled. Point it at a folder of Applications instead, which is what the bootstrap template does.

targetRevision set to HEAD

That follows whatever the default branch points at right now. Any merge, from anyone, changes production immediately. Name a branch you protect, or a tag.

Leaving self-heal off

Without it, a hand edit stays on the cluster and Argo CD shows a yellow badge that everyone learns to ignore within a fortnight.

No AppProject

Every Application then runs with everything Argo CD itself can do, and any repository anyone adds can deploy anything. A project limits which repositories count, which namespaces they reach, and which cluster-wide kinds they create. It cannot make ClusterRoleBinding safe, so give services that need one their own project and keep that project's repository list short.

Unpinned Helm charts

A floating chart version changes the cluster whenever the chart author publishes. Pin it, and let a dependency bot open the pull request when a new one comes out.

All three

Treating "it applied" as "it works"

Applying cleanly proves the tool did what the file said. It does not prove the file said the right thing. Every template page here ends with one command that proves the actual result, and it is the step worth not skipping.

Registry 0.6.0. Built 2026-09-22.

Made by Habibullah Tora. Code under the MIT licence, writing under CC BY 4.0.