How many of you have started with Terraform and discovered you need the same code to build multiple environments? Perhaps a dev, stage, and production environment? After researching Workspaces, Branches, and Terragrunt, you aren’t completely satisfied and want to know if there is another way. If you are here, I’ll assume that’s you!
Workspaces are a native Terraform construct, offering a reduction in duplicate code and methods of configuring environments differently based on the Workspace name. However, there are obstacles in navigating between environments, backend segregation, and versioning between Workspaces.
Not to mention, Hashicorp themselves do not recommend using Workspaces for managing environments:
“Workspaces alone are not a suitable tool for system decomposition, because each subsystem should have its own separate configuration and backend, and will thus have its own distinct set of workspaces.”
Branches are a native VCS (Version Control System) construct and allow for versioning to be part of your deployment model. A step up from Workspaces, Branches allows you to configure environments using [.code].tfvars[.code] files, configure different backends and configure versions. However, there is a huge duplication of code, and depending on your development strategy; propagating changes can be difficult.
Terragrunt is a Terraform wrapper that solves many of the native Terraform pain points. Environment segregation is easier by using a standard folder structure and variable files which can differentiate not only the resources being deployed but also the segregation of remote backends. Not to mention, versioning within the environments is also a lot easier than in either the Workspace or Branch model.
That said, this is a new tool that has its own method of operation. This has to be learnt and understood along with Terraform, as Terraform is run under the hood. There is slight code duplication, but nothing as bad as branches.
There is a great article by Gruntwork that we’d recommend reading, that goes into greater detail on the above points.
We covered three methods of how to manage different environments using Terraform and Terragrunt. While they all work, it can become complex when working in larger teams. Here at env0, we want to empower your team with all the options previously discussed, but make it simple and easy to use and consume.
First, let’s recap the major pain points we are trying to solve:
In env0 we have a three-tiered application structure, organization, project, and environment (think workspace). In most cases, our customers will use one organization and name it after the company, and then choose to use multiple projects. An example of project segregation would be dev, pre-production, and production.
This is all well and good, but how does this help with my code? Good question.
env0 has a concept of a template, which is described as a self-service mechanism. This provides several key benefits both from a coding and reusability perspective.
Let’s take an EC2 instance as an example. When writing code for an EC2 instance, a common number of variables need to be changed per environment or scenario. Imagine a developer who wants to test new application code on an EC2 instance. What do you do? Copy existing EC2 instance code to spin up the instance, or better yet, reference a module to spin up the desired EC2 instance. Let’s make life easier for you, let’s use an env0 template that references your code in your VCS (private or public). Here we can import variables from your code at the click of a button and change them as we prepare the template. We can even make cool changes like adding drop-down menus, for example, sizing.
This allows non-Terraform experts to spin up an EC2 instance, using Terraform code while changing the desired name and instance size through a UI.
This concept of templates allows you to reuse the same code in dev, pre-production, and production while changing a few key variables through a UI (or via code if you prefer). You can also use the same code to spin up multiple instances in a single area (i.e. dev). What’s really useful here is, a change to the template code can trigger a plan or apply to all environments (EC2 instances) using this template.
env0 takes advantage of Terraform workspaces behind the scenes and helps you easily manage the use of workspaces while giving you the ability to see what is deployed where.
Note: A single template can be the source for more than one environment (EC2 instance).
Furthermore, the power of templates is not subject to small objects, such as EC2 instances. Templates can be used to spin up VPCs or even full infrastructure stacks.
The variable importing and editing is a cool feature, but what else can it do? In env0 you can define variables at any level of the application structure. This means you can define a variable in the project to specify the region you want the resources deployed within the environment (workspace). Furthermore, you can mark this as a read-only variable, so users of the template can’t change this variable and even mark it as sensitive should you be adding a project token for example.
During the template run, you will clearly see which variables have been inherited by the template and which have been inherited by the project.
I hear what you are saying, that’s all well and good, but what about versions or branches of the same code? Don’t worry, we have you covered. Our templates can pull code from any branch or tag in your VCS. You can update the branch both at a template level or an environmental (workspace) level.
Intrigued yet? Keep going, it gets better.
The final feature we’ll discuss today is env0 workflows. We see users move from Terraform to Terragrunt (both of which are supported as first-class citizens in env0) to help with the build of IaC stacks. No one wants to run code to deploy a VPC, move to the security folder, run the security code, and so on. We are in full agreement, Terragrunt can help, and this is the reason we not only support Terragrunt code but also the all-important Terragrunt run-all feature.
All this Terragrunt talk is exhausting, so let's see how env0 can help. Whether you are writing in Terraform, Terragrunt, Pulumi (yes, we support Pulumi too) or even creating Kubernetes manifest files, env0 can execute the code in order depending on your dependencies defined in an env.yaml file. I think images paint a thousand words, so below we have our env0 workflow that will execute from left to right, not moving forward until the parent has successfully deployed.
We know, it’s pretty cool, right?!
We started this blog with three issues.
We solved environment segregation using our three-tiered application structure.
We solved code reusability with templates, variables, and revisions.
We solved dependencies using env0 workflow logic.
We would love to hear what you think, about the approaches here, or even how you tackle your environment management.