This post was written by Rich Bosomworth.
Although an excellent tool for infrastructure orchestration, Terraform plans can quickly become complex and difficult to manage, especially when deploying to multiple environments. In this post we examine the benefits of abstracting away core aspects of the deployment process into a GoCD wrapper and look at the enhanced potential offered by Terraform Enterprise. A basic understanding of Terraform and Continuous Delivery concepts is assumed.
At Skelton Thatcher Consulting we use Terraform for deploying our Rancher stack to AWS. As our stack is deployed to a single environment this means that the structure and management of the Terraform plan are both quite straightforward. For example, without multiple plans we have no need for shared modules, nor do we require a multi-folder hierarchy to facilitate environment and plan segregation. Plan state (.tfstate) is also a simple single source output, either local, or to remote state in AWS S3 (we recommend implementing s3 remote state, even for single user managed deployments).
With larger, multi-environment deployments an extra layer of modules are (sensibly) introduced to minimise code replication and to facilitate resource parity across (say) dev/test/prod. However, concerns over statefile management soon arise, even more so should an upper/lower (network/compute) split plan model be introduced, and more so again if there is a multi-region requirement. There are many articles advising on logical structures for multi-environment Terraform plans, although for us the majority of solutions offered still feel overly complex and difficult to manage.
A recent project included a client request to implement dynamic environments for AATs using a combination of Terraform and Ansible. To help facilitate this we abstracted the Terraform run level operations into a GoCD pipeline, with the plan being pulled fresh at each run from a single source (Git) as a material item. The GoCD run dynamically created an individually named remote statefile artifact (in our case based on the run descriptor), passed in required vars, and concurrently tagged all resources (again, with the run descriptor).
Through implementing this model, we realised the benefits it offered for enhanced management of Terraform runs in general, especially across multiple environments.
- With a standard multi-environment structure (i.e dev/test/prod), deploying to a single cloud vendor (even across regions) can be achieved from a single Terraform plan.
- The additional configuration and management of module components is not required with a single plan (there would be no benefit in doing so).
- A single plan means that there is only one set of .tf files to maintain and version control.
- The passing (in) of variables can be simplified, with a regular three level environment requiring no more than the following six entries:
- S3 remote state key name (for specific tfstate)
- AWS Region
- Env name/id (i.e dev/test/prod)
- VPC/subnet(s) & CIDR range(s)
- AMIs (because AMIs are region specific)
- EC2 instance amounts
- Having .tfstate in a controlled reference environment opens up options for implementing state drift detection (as described in this excellent post from Build ACL).
During our time on the project engagement, Hashicorp themselves released a beta of Terraform Enterprise. This new product offers enhanced governance against the limited scope of the open source version and a feature set in line with our own approach, which was affirming.
Although we achieved a satisfactory level of managed deployment for our dynamically generated Terraform environments using GoCD, it will be interesting to see how much Terraform Enterprise can streamline the process and we are looking forward to evaluating its extensive feature set across teams.