Advanced Terraform: Getting Started With Terragrunt
What Is Terragrunt?
Terragrunt is an open source tool developed by Grunt Work that helps to reduce code duplication across your Terraform projects - effectively keeping your terraform code DRY.
DRY is a popular acronym which means Do not Repeat Yourself.
Terragrunt can be used to manage Terraform code across multiple environments, multiple AWS accounts, handling dependency management, custom actions and versioning.
Terragrunt simplifies the management of multiple environments by providing a clear separation between them.
Terragrunt hooks can be used to perform actions before or after Terraform commands.
Terragrunt can be integrated with your CI/CD pipelines for automated infrastructure deployment.
Terragrunt is able to integrate with external secret management tools like Hashicorp Vault or AWS Secrets Manager for managing sensitive data.
In fact, you can use Terragrunt commands in place of basic terraform commands.
terragrunt init
terragrunt plan
terragrunt apply
terragrunt output
terragrunt destroy
Terragrunt lets you specify the IAM role to use. You can do this by using the _--terragrunt-iam-role_
CLI argument or the TERRAGRUNT_IAM_ROLE environmental variable. Terragrunt will call the sts-assume-role API and then expose the credentials it receives as environmental variables when invoking terraform. This makes it possible to seamlessly deploy infrastructure across different environments without having to store AWS credentials in plaintext.
Installing Terragrunt
To install terragrunt, make sure you have alredy installed terraform beforehand.
Follow this link to install terraform.
Download the Terragrunt binary from the releases page.
Select the correct binary for your OS.
Copy the link and download on your terminal using the wget command. Example:
wget
https://github.com/gruntwork-io/terragrunt/releases/download/v0.54.19/terragrunt_linux_amd64
Rename the binary to terragrunt:
mv terragrunt_linux_amd64 terragrunt
Make the file executable:
chmod u+x terragrunt
Move the file to the
/usr/local/bin
directory: `sudo mv terragrunt /usr/local/bin/terragrunt'To verify your installation, run:
terragrunt --version
.
A Case Study
Let us now present a scenario for managing infrastructure with Terragrunt.
The following is a sample terraform code for creating an EC2 instance using Terraform.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "app_server" {
ami = "ami-830c94e3"
instance_type = "t2.micro"
tags = {
Name = "ExampleAppServerInstance"
}
}
The problem is, how do you create this instance across several environments?
Ordinarily, you would want to duplicate the same code for each environment and then modify the parameters.
You might wish to specify different instance types for each environment. For example, you might want to use a t2.micro instance for development environment, a t3.medium for test environment and a t2.large for production.
The problem is that this would be a very manual process -inefficient and leaves plenty room for error. Using Terragrunt, we can improve this code without having to copy and paste the code across several environments.
Also these environments might be spread out across different AWS accounts and you'd also have to worry about managing the different AWS credentials and IAM roles required.
The terraform backend does not support variables or any type of expression whatsoever. So you would have to manually copy and edit the backend configuration code for each of the environments.
Working With Terragrunt
First we need to create separate directories for each of the environments. In each of the directories, a terragrunt.hcl
file will be created. This is the configuration file for each of the environments.
Your directory structure should look like this:
First, we have to define a terraform block and declare the source of our EC2 module.
`
terraform {
source = "tfr:///terraform-aws-modules/ec2-instance/aws?version=5.6.0"
}
Now let's define a provider.
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
provider "aws" {
profile = "default"
region = "us-east-1"
}
EOF
}
`
Now we need to declare an inputs
block. This is where most of the magic of Terragrunt happens.
we can simply define one terraform code for all our environments and then use the inputs
block to change values as required across all the environments.
inputs = { ami = "ami-0005e0cfe09cc9050" instance_type = "t2.micro" tags = { Name = "grunt-ec2" } }
What is happening here is that the instance type, ami and tags are values that are intended to differ based on the environment.
My terragrunt.hcl
file now looks like this:
I have replicated this in my test
directory with a few changes. The instance type is t2.medium, the tags have also been modified to reflect the environment.
The terragrunt.hcl
file in my test environment looks like this:
The next stage is to execute Terragrunt commands to read our configuration and invoke terraform with it.
Switch to the
dev
directory.First, run
terragrunt init
.Now run
terragrunt plan
to see proposed infrastructure changes. This is how mine looks:
- Run
terragrunt apply
to actually create the instance.
Repeat the steps above for the test environment.
Final Thoughts
If you followed this tutorial until now, I'm sure you must have begun to see the importance of Terragrunt and how it helps manage terraform code across different environments while keeping your terraform code DRY.
Subscribe to my newsletter
Read articles from ProDevOpsGuy Tech Community directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
ProDevOpsGuy Tech Community
ProDevOpsGuy Tech Community
๐ช๐๐๐๐ ๐๐๐ ๐ซ๐๐๐ถ๐๐ ๐ป๐๐๐ ๐ช๐๐๐๐๐๐๐๐ || ๐ท๐๐๐ซ๐๐๐ถ๐๐๐ฎ๐๐ ๐ค https://t.me/prodevopsguy ๐ Hi there! We are ProDevOpsGuy, a passionate DevOps enthusiast Tech Community with a strong belief in the power of automation and collaboration to drive innovation. ๐ I thrive in bridging the gap between development and operations, creating seamless and efficient software delivery pipelines. My journey in the world of DevOps has allowed me to blend my technical skills with a knack for problem-solving, enabling me to contribute effectively to agile and dynamic environments. ๐ก With a keen interest in continuous integration, continuous delivery (CI/CD), containerization, and orchestration, I've had the privilege to explore cutting-edge technologies like Docker, Kubernetes, Jenkins, and Ansible. I find joy in designing scalable and resilient infrastructures that enable teams to deploy applications faster and with greater confidence. ๐ Beyond the tech realm, I'm an advocate for DevOps culture, emphasizing collaboration, communication, and a relentless pursuit of improvement. I'm always eager to connect with fellow professionals, exchange insights, and explore opportunities to collaborate on exciting projects. ๐ When I'm not tinkering with the latest DevOps tools, you can find me indulging in books on technology trends, hiking to rejuvenate, and occasionally experimenting with new coding challenges. ๐ Let's connect! Whether you're looking to discuss DevOps methodologies, explore partnership opportunities, or simply share experiences, feel free to reach out. I'm excited to be part of the DevOps journey, driving excellence together.