When creating an IaC repo for your organization, there are a number of architecture decisions that need to be made. One of the most important you can choose is how to build your repository structure… should you go multi-repo or mono-repo?
The one you chose for your organization is dependent on a number of factors, including the purpose of the IaC in the first place. Is it for development? Will they be deployed and managed in production? How is your application code architected? Does your infosec team weigh in here, too?
Sometimes you are graced with the ability to plan ahead and build out a longer-term repo strategy before the first commit is ever made. However, past design decisions often corner you into a particular strategy that proves limiting over time. Specifically affecting how your codebase interacts with external tooling can end up being a major source of frustration.
The good news is that whatever your primary repository strategy–mono repo or multi repo–env0’s leading IaC management platform is ready to help. In this article, we’ll walk you through how env0 works with your repo strategy, and how to use env0 effectively with each strategy type.
env0 structure
First, let’s ensure we’re on the same page with general structure and terminology.
- Templates are the way to reuse TF code, and also configuration
- Projects are the way to manage permissions, policies, and also reuse configuration
- Environments are an instance of a template in the context of a project
- For static environments - we expect one per template+project duo (e.g. a VPC environment, created from VPC template in a Prod project)
- For ephemeral environments - we can expect multiple (e.g. a Lambda function, created from a Lambda template, in a QA project)
- Regardless of how projects/templates are set up - each environment will run on it’s own TF Workspace by default - so states will be kept separate.
The ideal way to use env0 is writing ‘context agnostic’ TF code, and using Projects to set the context. While this is the ideal, many teams have already written some Terraform code, and their repo structure is different. Read on to see your options.
Terminology
- Service - A subsystem/component of your infrastructure. e.g. frontend, backend, databases. There are many ways to split up a system, this could also be “compute / data / networking”
- Stage or Environment - A different deployment stage (or CI/CD environment) of your system - e.g. production, staging, dev, qa. To avoid ambiguity with env0’s environment, we’ll use Stage for the rest of this post.
- Deployment - A single Terraform code root module - a group of TF files that are applied together.
- State - The Terraform state file, which is usually per service and stage.
- `main.tf` - Terraform Deployment entrypoint, usually named main but doesn’t have to be.
Remote backend is a must
Regardless of your selection, one part is imperative: you should be using a remote backend in order to store your Terraform state. This means that:
- You keep total control of your own state files in a cloud of your choice..
- You have direct access to your state files.
- You own access controls to your state files.
- You’ll retain the option to run your Terraform deployment locally if needed.
- This is mandatory if you are migrating existing states to env0.
Check out our documentation to learn more about remote Terraform backends and migrating existing Terraform state into env0.
Repo structures
One big deployment
Although this typically is an anti-pattern, migrating one big Terraform deployment to env0 is pretty straight forward—just create a template pointing to your main.tf, and run that in env0. This misses out on a lot of things - for example RBAC will be the same for everyone, since there is only one project.
Why is this an anti-pattern? These repo types often create unnecessary interdependencies. A failure in one module or resource cascades, and may affect other non-dependent resources. For instance if you’re slow to deploy a change, it’s possible that a small PR could be blocked by another larger PR. This happens pretty frequently with Atlantis’ preferred repo model.
Deployment per service, states separated by workspaces.
- You have a separate `main.tf` for each service.
- You use Terraform workspaces to separate the states for the different stages.
- You use .tfvar files to separate the configuration for the different stages. (optional)
- Example
This is a ‘classic’ env0 structure. Create a template for each service, and a project for each stage. That way you can enjoy different RBAC and policies for each stage. For example - you’ll have a ‘Production’ project that most users can only view, and where ‘destroy’ is disabled for everyone. The ‘QA’ project will have different configuration settings, and members of your QA team can have full control over their environments. Optionally, you can store your config in code (tfvar) or you can use env0 variables.
Deployment per stage
- You have a separate `main.tf` for each stage.
- Example
Create a template for each stage - you’ll have multiple templates connected to the same repository, but configured for different folders.
Create a different project for each stage - to enforce RBAC and policies per stage.
You can set up your configuration variables based on template OR project, but we recommend doing so per Project - so Project RBAC is applied on the configuration as well.
You’ll have a single service per template + project.
Deployment per Stage & Service
- You have a separate `main.tf` for each stage & service.
- Example
- Terragrunt Example
This is the most common structure for Terragrunt users. This is similar to “deployment per stage” but you’ll have more templates, as you would need to create one per stage/service combination.
In env0, we still recommend creating a single project per stage and deploying all the services under that stage to that project, as it usually makes more sense to control RBAC like that.
You’ll have a single service per template. In the near future, env0 will allow you to create an environment without having to create a template.
Complex Stages
In case of very complicated setups, we recommend splitting stages up to sub-stages, for example by region or cloud provider. Since env0 does not support “sub-projects” or a similar division, these would just be set up as separate projects, e.g. “Prod - us-east-2” or “Development - Azure”.
Summary
The good news is that regardless of your repo selection choice, there is a way to easily manage your Terraform at scale with env0. While your current repo architecture may not be ideal, if re-factoring is too impactful, there’s a good chance that using Terraform automation and Collaboration Software (aka TACOS) like env0 can help smoothe over the bumps.
Happy deploying!
When creating an IaC repo for your organization, there are a number of architecture decisions that need to be made. One of the most important you can choose is how to build your repository structure… should you go multi-repo or mono-repo?
The one you chose for your organization is dependent on a number of factors, including the purpose of the IaC in the first place. Is it for development? Will they be deployed and managed in production? How is your application code architected? Does your infosec team weigh in here, too?
Sometimes you are graced with the ability to plan ahead and build out a longer-term repo strategy before the first commit is ever made. However, past design decisions often corner you into a particular strategy that proves limiting over time. Specifically affecting how your codebase interacts with external tooling can end up being a major source of frustration.
The good news is that whatever your primary repository strategy–mono repo or multi repo–env0’s leading IaC management platform is ready to help. In this article, we’ll walk you through how env0 works with your repo strategy, and how to use env0 effectively with each strategy type.
env0 structure
First, let’s ensure we’re on the same page with general structure and terminology.
- Templates are the way to reuse TF code, and also configuration
- Projects are the way to manage permissions, policies, and also reuse configuration
- Environments are an instance of a template in the context of a project
- For static environments - we expect one per template+project duo (e.g. a VPC environment, created from VPC template in a Prod project)
- For ephemeral environments - we can expect multiple (e.g. a Lambda function, created from a Lambda template, in a QA project)
- Regardless of how projects/templates are set up - each environment will run on it’s own TF Workspace by default - so states will be kept separate.
The ideal way to use env0 is writing ‘context agnostic’ TF code, and using Projects to set the context. While this is the ideal, many teams have already written some Terraform code, and their repo structure is different. Read on to see your options.
Terminology
- Service - A subsystem/component of your infrastructure. e.g. frontend, backend, databases. There are many ways to split up a system, this could also be “compute / data / networking”
- Stage or Environment - A different deployment stage (or CI/CD environment) of your system - e.g. production, staging, dev, qa. To avoid ambiguity with env0’s environment, we’ll use Stage for the rest of this post.
- Deployment - A single Terraform code root module - a group of TF files that are applied together.
- State - The Terraform state file, which is usually per service and stage.
- `main.tf` - Terraform Deployment entrypoint, usually named main but doesn’t have to be.
Remote backend is a must
Regardless of your selection, one part is imperative: you should be using a remote backend in order to store your Terraform state. This means that:
- You keep total control of your own state files in a cloud of your choice..
- You have direct access to your state files.
- You own access controls to your state files.
- You’ll retain the option to run your Terraform deployment locally if needed.
- This is mandatory if you are migrating existing states to env0.
Check out our documentation to learn more about remote Terraform backends and migrating existing Terraform state into env0.
Repo structures
One big deployment
Although this typically is an anti-pattern, migrating one big Terraform deployment to env0 is pretty straight forward—just create a template pointing to your main.tf, and run that in env0. This misses out on a lot of things - for example RBAC will be the same for everyone, since there is only one project.
Why is this an anti-pattern? These repo types often create unnecessary interdependencies. A failure in one module or resource cascades, and may affect other non-dependent resources. For instance if you’re slow to deploy a change, it’s possible that a small PR could be blocked by another larger PR. This happens pretty frequently with Atlantis’ preferred repo model.
Deployment per service, states separated by workspaces.
- You have a separate `main.tf` for each service.
- You use Terraform workspaces to separate the states for the different stages.
- You use .tfvar files to separate the configuration for the different stages. (optional)
- Example
This is a ‘classic’ env0 structure. Create a template for each service, and a project for each stage. That way you can enjoy different RBAC and policies for each stage. For example - you’ll have a ‘Production’ project that most users can only view, and where ‘destroy’ is disabled for everyone. The ‘QA’ project will have different configuration settings, and members of your QA team can have full control over their environments. Optionally, you can store your config in code (tfvar) or you can use env0 variables.
Deployment per stage
- You have a separate `main.tf` for each stage.
- Example
Create a template for each stage - you’ll have multiple templates connected to the same repository, but configured for different folders.
Create a different project for each stage - to enforce RBAC and policies per stage.
You can set up your configuration variables based on template OR project, but we recommend doing so per Project - so Project RBAC is applied on the configuration as well.
You’ll have a single service per template + project.
Deployment per Stage & Service
- You have a separate `main.tf` for each stage & service.
- Example
- Terragrunt Example
This is the most common structure for Terragrunt users. This is similar to “deployment per stage” but you’ll have more templates, as you would need to create one per stage/service combination.
In env0, we still recommend creating a single project per stage and deploying all the services under that stage to that project, as it usually makes more sense to control RBAC like that.
You’ll have a single service per template. In the near future, env0 will allow you to create an environment without having to create a template.
Complex Stages
In case of very complicated setups, we recommend splitting stages up to sub-stages, for example by region or cloud provider. Since env0 does not support “sub-projects” or a similar division, these would just be set up as separate projects, e.g. “Prod - us-east-2” or “Development - Azure”.
Summary
The good news is that regardless of your repo selection choice, there is a way to easily manage your Terraform at scale with env0. While your current repo architecture may not be ideal, if re-factoring is too impactful, there’s a good chance that using Terraform automation and Collaboration Software (aka TACOS) like env0 can help smoothe over the bumps.
Happy deploying!