Terraform best practices that you should use in your project
For a long time, manual operations was the only way to manage, create, configure servers but recently with the rise of cloud and Devops best practices, we shifted to a new set of paradigms and technologies that made those operations easier and more secure with lower risks and costs. These practices are known under the name of Infrastructure as code (IaC), as its name implies it consists of storing infrastructure elements as code which make them reusable, versioned and more consistent.
One of the most used tools among developers to manage Infrastructure as code is Terraform which was created and maintained by Hashicorp and it allows you to define resources on premises and also on multiple cloud providers. In this article we will do a quick tour on the different best practices of Terraform.
The most important building block of terraform is the State that is tracked inside a state file that has an extension of .tfstate so any changes to the current resources will be compared to the content of the tfsate to define the set of actions that are required to reach the desired state.
Do not edit state file manually
Developers should not modify the state file manually as this can corrupt it and also the file will not mirror anymore the actual state of resources which contradict the purpose of its existence and can lead to unexpected errors. So we are only allowed to manipulate that state file through TF commands like terraform apply.
Do not commit the statefile onto your version control system
Terraform state can contain sensitive data like login details, passwords, resource key attributes, etc... So we need to avoid storing them as plain text in our version control system as anyone inside the organization can access them. Moreover every developer who cloned the project would have those secrets stored on his hardware which makes revoking them or controlling the way they are stored an impossible task to do and also this creates a multiple vulnerable points as if a developer’s PC is hacked those secrets will be accessible to malicious peoples that can use them to put the enterprise at risk.
There are multiple approaches to tackle this problem like using environment variables or using encryption to hide them. But the best approach is to use a remote state backend that resides on the cloud and to store those secrets inside a vault (Azure Vault or Amazon Secrets Manager, etc…). Here is a link to how to do it on azure as an example.
The latest approach also simplifies and secures the way developers can fetch the latest state file and ensures that its content is coherent between them but this can lead to a problem of conflict that arises when two developers try to update the remote state at the same time. so it’s recommended to use a state lock for those situations which prevents concurrent edits. But not all remote backends support this behavior so you need to take that into account when choosing one.
Use Environment specific state files
During software development lifecycle, developers use multiple environments like dev, staging and production and every one of them has its own separate set of resources so it makes more since to have separate state file. Also any change in any environment’s specific resource should not in any way impact the other environments as an example if the state of development changes it should not cause any regression on production.
The second set of practices concerns the Modules which represents a self-contained packages of terraform configurations that are managed as a group, as an example, you can create or use an open source module that allows you to create a security group without worrying too much about the underlying complexity.
Avoid being too specific when writing your modules
Most of the developers tend to write modules that solve their own specific cases without worrying too much about its reusability and the ability to solve similar problems inside the organization.
For example in the code below the developer assumed that everyone using his module should only use Europe east as a region otherwise he should write his own module.
So as a best practice developers should try to put provider configurations as high as possible to allow others to specify the ones that suit their use cases.
Another common bad practice is to tightly couple the vpc resource alongside with a provisioner which allows to run shell or CLI commands after the resource is being allocated. So if anyone wants to reuse this module, he should have exactly the same commands that are run by the provisioner otherwise there is no way for reusing or extending them.
So you should avoid writing provisioner sections and try to use external tools outside of terraforms like puppet or any other tool that allows you to run these commands.
Common traits of good module:
The below list contains a little of relevant points that every developer should consider when writing terraform modules
Documentation and examples: which is the most important point as it facilitates the task for other developers who wants to reuse your module and tells them what to expect from it and what defaults does it have.
Make it as generic as possible: you should try to avoid hardcoding any configuration that can impact the reusability of your module.
Clean code it makes sense to respect best code practices like SOLID principles to make your code more readable and reusable.
Automated tests to secure any future changes and to make sure not to deteriorate the current behavior.
In the above article we tried to present some of the best practices that terraform developers should use in their projects. Please let us know if you see other practices that you have put in place in your projects to enrich the list.
Thanks for reading :)
Fadi