overlapping but not conflicting capabilities with Terragrunt, we designed Terramate so that it can be integrated and used in any existing project and with any third-party tooling that you already use and love - in a non-intrusive way and without changing any configuration.
In a nutshell, Terramate is an Infrastructure as Code management platform consisting of an open-source CLI that optionally pairs with a fully managed cloud service. One of the most important features in the Terramate CLI is orchestration, which allows you to orchestrate any command in stacks. That means you can orchestrate Terragrunt and, subsequently, Terraform along with Terramate.
Yip, you read this right. Terramate and Terragrunt work well together!
But why should you consider adding Terramate to your Terragrunt Project?
With Terragrunt, you can only execute a single or all modules (e.g., terragrunt run-all apply
and terragrunt run-all apply
). This becomes challenging, especially when running Terragrunt in CI/CD. Terragrunt doesn't have any advanced orchestration functionality, and due to the lack of change detection, it is impossible to detect and execute modules that have changed, which can lead to long execution run times and a large blast radius.
You can overcome this by orchestrating Terragrunt using the Terramate orchestration and change detection.
For example, the following command orchestrates terragrunt apply
in all Terragrunt modules that contain changes:
terramate run --changed -- terragrunt apply
The change detection capabilities in Terramate are based on Git and cover Terraform, OpenTofu, and Terragrunt-specific use cases:
include
blocks)dependency
and dependencies
blocks)read_terragrunt_config()
, read_tfvars_file()
, etc)terraform.source
blocksIn addition, Terramate orchestration can be leveraged to run any command **in Terragrunt modules. Remember, Terragrunt is a wrapper that doesn’t allow you to run custom commands.
Automating Terragrunt in CI/CD is hard, and no pre-configured integration exists. Even worse, most of the examples out there want you to execute terragrunt run-all apply
in every single pipeline run, which means you would always execute all modules, defeating the value add of using multiple root modules (aka stacks) in the first place.
Terramate CLI integrates well with GitHub Actions, GitLab CI/CD, and Bitbucket Pipelines, allowing you to orchestrate and automate Terragrunt with fully configurable GitOps workflows securely and cost-effectively by reusing compute and security already in place. With Terramate, you can configure apply-and-merge
or merge-and-apply
workflows, provide previews in Pull Requests and configure custom approval workflows using policies.
The example below shows how plan previews look in GitHub Pull Requests using Terramate in GitHub Actions:
Terragrunt is often used to simplify and standardize environments at scale. While this is great, it can be challenging for teams to keep an overview of what infrastructure is managed in Terragrunt modules, especially when working with multiple repositories.
Terramate Cloud provides an overview of all your Terragrunt modules, deployments, drift detection runs, and more, which improves visibility, insights, and observability.
This works because Terramate CLI can sync data from orchestrated commands such as terragrunt plan
to Terramate Cloud. For example, the following command creates a plan in each Terragrunt module and syncs those to Terramate Cloud.
terramate run \
--parallel=5 \
--cloud-sync-preview \
--cloud-sync-terraform-plan-file=out.tfplan \
--terragrunt \
-- \
terragrunt plan -out out.tfplan -detailed-exitcode
Execution is done in parallel, and syncing plans to Terramate Cloud allows us to review plans per stack. It’s important to understand that Terramate never syncs sensitive values to Terramate Cloud. Instead, Terramate CLI removes all sensitive values, such as secrets, and only a sanitized version of the plan is synced. Terramate Cloud does not have or require access to your state files, code, or cloud accounts, making it very secure to use.
Drifts in IaC are untracked changes, meaning your deployed resources have changed and no longer match the desired configuration. Such untracked changes pose risks with varied severity and have the potential to drastically impact your managed infrastructure's reliability, security, or performance. With Terramate, you can use scheduled workflows in your CI/CD to detect and reconcile drift.
The drift detection works by running a scheduled terragrunt plan
on all stacks and sending the results to Terramate Cloud. The following command runs a drift detection in all Terragrunt modules and syncs the result to Terramate Cloud.
terramate run \
--cloud-sync-drift-status \
--cloud-sync-terraform-plan-file=drift.tfplan \
--continue-on-error \
--terragrunt \
-- \
terragrunt plan -out drift.tfplan -detailed-exitcode -lock=false
You can then reconcile drifted stacks by using the --cloud-status
filter in the Terramate orchestration to run terragrunt apply
in all drifted stacks:
terramate run \
--cloud-status=drifted \
--cloud-sync-deployment \
--cloud-sync-terraform-plan-file=drift.tfplan \
--terragrunt \
-- \
terragrunt apply -input=false -auto-approve -lock-timeout=5m out.tfplan
If you only want to enable auto-reconciliation for specific stacks, you can archive this using tags. Additionally, you can configure manual approval workflows to review a reconciliation workflow before running it.
Alright, let’s get started. In this guide, we will use Gruntwork’s well-known reference architecture. It’s an example of using Terragrunt in a production-grade manner and the perfect starting ground to explore how Terramate can provide immediate value to any Terragrunt project!
If you want to see the end result of this guide right away instead, feel free to take a look at our fork of the reference architecture that comes with all of the mentioned steps implemented below. It gives you an idea how the integration works.
Before we can dive in, we need to install Terramate, Terragrunt, and Terraform. You also need a working AWS account to deploy the infrastructure configured in the reference architecture. Please note that you can easily replace Terraform with OpenTofu if you wish.
v0.6.1
or newer.v0.55.0
or newer.v1.7.5
or OpenTofu v1.6.2
or newer.After setting up the required dependencies and creating your Terramate Cloud account, we can continue configuring and deploying the Terragrunt reference architecture with Terramate.
Next, let’s clone the terragrunt-infrastructure-live-example repository so we can start getting our hands dirty.
git clone https://github.com/gruntwork-io/terragrunt-infrastructure-live-example
To configure Terramate in the repository, add the terramate.tm.hcl
file in the root of the repository with the following configuration. Please replace the TG_BUCKET_PREFIX
environment variable with a prefix of your choice and add the identifier of your Terramate Cloud organization.
terramate.tm.hcl
terramate {
config {
run {
env {
TG_BUCKET_PREFIX = "yourprefix-" # TODO: replace this with a unique prefix
}
}
git {
# Git configuration
default_remote = "origin"
# Safeguards
check_untracked = false
check_uncommitted = false
check_remote = false
}
# Configure the namespace of your Terramate Cloud organization
# Set this to the short name of your Terramate Cloud Organization
cloud {
organization = "terramate-demo"
}
}
}
This configuration takes care of a few things:
TG_BUCKET_PREFIX
environment variable using Terramate to configure the running environment. Make sure that you replace yourprefix-
with a prefix of your choice.terragrunt apply
when you have files or changes that aren’t checked into your repository, and help you avoid applying code changes without having peers review those first. For production environments, we recommend you turn those on!Gruntworks’ repository uses master
as the default branch. Let’s update this to main and remove all ties to the upstream repo.
git checkout -b main
git config --global init.defaultBranch main
git branch -D master
git remote remove origin
We will use a single AWS to deploy the production and all pre-production environments such as prod
, stage
and qa
.
Update the account.hcl
file in prod/account.hcl
as well as non-prod/account.hcl
and set aws_account_id
to your AWS account ID.
locals {
aws_account_id = "XXXXXXXXXXXXXXX"
}
Et voila, the reference architecture is now configured and ready to deploy. Let’s take a look at how we can trigger the initial deployment with Terramate.
In Terramate, features such as orchestration are based on stacks, which, at the bare minimum, are just directories that contain a stack.tm.hcl
file, which can be used to configure a stack's metadata and orchestration behavior.
For Terramate to detect and orchestrate Terragrunt modules, we must declare all Terragrunt modules as stacks. To make this process as simple and fast as possible, Terramate comes with a built-in command. The create —-all-terragrunt
command scans all available Terragrunt modules in your current repository and creates a stack.tm.hcl
in each detected module.
terramate create --all-terragrunt
Created stack /non-prod/us-east-1/qa/mysql
Created stack /non-prod/us-east-1/qa/webserver-cluster
Created stack /non-prod/us-east-1/stage/mysql
Created stack /non-prod/us-east-1/stage/webserver-cluster
Created stack /prod/us-east-1/prod/mysql
Created stack /prod/us-east-1/prod/webserver-cluster
This is it! We can now use Terramate in our Terragrunt project. Try a few of the available commands to see how Terramate works.
List all stacks:
terramate list
Understand the order of execution:
terramate list --run-order
Orchestrate a command in all stacks
terramate run -- echo "hello world"
Now that we have our project configured let’s continue and deploy the infrastructure configured in prod
. Since the reference architecture deploys a MySQL instance, we need to configure the DB password as an environment variable:
export TF_VAR_master_password="REPLACE_ME"
Note: We use the same password for all databases deployed in this example. In a real-world scenario, you should probably use different passwords for each database.
Next, let’s run terramate run terragrunt plan
to see the changes you will apply. In a nutshell: Terramate will traverse the dependency graph of Terragrunt modules and orchestrate the terragrunt plan
in all modules sequentially.
Up on the initial orchestration of the terragrunt plan
command, Terragrunt will create the Terraform state bucket for you if it’s non-existent.
terramate run terragrunt plan
terramate: Entering stack in /prod/us-east-1/qa/mysql
terramate: Executing command "terragrunt plan"
Remote state S3 bucket terragrunt-example-tf-state-prod-us-east-1 does not exist or you don't have permissions to access it. Would you like Terragrunt to create it? (y/n)
...
After reviewing the plan of each stack for correctness, you can apply the changes.
terramate run terragrunt apply
Congratulations, you just deployed the reference architecture.
Next, let’s sync our data to Terramate to get an overview of all our stacks.
To archive this, we need to authenticate Terramate CLI with Terramate Cloud.
terramate cloud login
This command authenticates the CLI by logging in to Terramate Cloud with the identity provider of your choice and will allow you to sync and fetch data to and from Terramate Cloud.
For example, terramate list --cloud-status=drifted
can be used to retrieve a list of stacks that are marked as drifted
from Terramate Cloud. You can also use the --cloud-status
attribute to select stacks for orchestration. For example, terramate run --cloud-status=drifted -- terragrunt apply
would run terragrunt apply
in all drafted stacks to remediate drift.
To initially sync all stacks to the cloud, the most straightforward way is to run an initial drift detection in all stacks:
terramate run \
--cloud-sync-drift-status \
--cloud-sync-terraform-plan-file=drift.tfplan \
--continue-on-error \
--terragrunt \
-- \
terragrunt plan -detailed-exitcode -out drift.tfplan
After this, all managed stacks are synced and available in the stacks section of Terramate Cloud.
In our repository you can find a few pre-configured GitHub actions workflows:
preview.yml
Runs terragrunt plan
for all changed stacks inside a Pull Request and provides a preview to review changes before merging to the main branch.deploy.yml
Runs terragrunt apply
on all changed stacks when merging back to the main
branch. We use the merge-and-apply
strategy in this example and automatically apply all changes.Copy those workflows to your repository and try introducing a change by, e.g., changing input parameters such as the allocated storage for each database. Once you create a new branch, commit the changes, and open a new Pull Request - Terramate will create a plan and associated preview for you inside the Pull Request and in Terramate Cloud.
Pull Request Preview
Terraform Cloud
You can send different outputs and previews to Terramate Cloud. For example, in addition to the Terraform plan you can send data and summaries from tools such as infracost, checkov, terrascan, tfsec and many others.
Using a Pull Request workflow to add changes and deploying those out on merge back to the main branch enables you to manage your cloud infrastructure fully automated by using GitOps principles. In addition, you can configure settings such as branch protection rules in GitHub to enforce reviews of changes introduced before allowing to merge PRs.
To enable the workflows, you need to create a GitHub Actions secret in your repository to configure the database password called MYSQL_PROD_MASTER_PASSWORD
. We inject the password as a secret to avoid hard-coding secrets in our Terraform Configuration.
In addition to the preview and deployment workflows explained above, you can find an additional workflow in the repository called drift-detection.yml.
This workflow runs a plan in all stacks every 6 hours and automatically remediates drift for stacks tagged with reconcile
. Whenever this workflow detects one or more drifted stacks, it will mark them as drifted
in Terramate Cloud and provide the drift details.
In addition, if you configure the Slack integration in Terramate Cloud, you will receive Slack notifications about detected drift.
We configured the drift detection to automatically reconcile drift in all stacks that are tagged with reconcile
. For example, see added the tag to all stacks in the prod in our repository.
terramate-terragrunt-infrastructure-live-example/prod/us-east-1/prod/mysql/stack.tm.hcl
stack {
name = "mysql"
description = "mysql"
id = "d28fe1cf-5ed0-4433-a939-34d6fe3fe473"
tags = ["reconcile"]
}
This is pretty useful because it allows you to configure whether stacks should be reconciled automatically or not. The handling of detected drift is also fully configurable. For example, you can easily add an approval step to the workflow to review a drift before reconciling it.
That’s it! In this guide, you learned how to use Terramate to overcome common challenges in Terragrunt. You can use this to quickly supercharge any Terragrunt project with orchestration, change detection, GitOps workflows, drift detection, observability, deployment notifications and more.
Feel free to give us feedback and ask any questions in our Discord community. You can also check out the example repository by cloning it and running it in your GitHub account.
Stay tuned for future articles. There’s a lot more stuff to learn that we haven’t covered in this article, such as the native code generation in Terramate, which helps you to keep your stacks DRY by generating files such as Terraform, OpenTofu, JSON, YML and more.