In this example, we will create a Terraform module for the AWS provider, which will be launched for 3 different environments using Terragrunt.
Requirements:
- Installed AWS CLI
- Installed Terraform (version above 12.0)
- Installed Terragrunt
Create the required files for Terragrunt:
terragrunt.hcl
terraform { extra_arguments "custom_vars" { commands = get_terraform_commands_that_need_vars() arguments = [ "-var-file=${get_terragrunt_dir()}/common.tfvars", ] } } remote_state { backend = "s3" config = { bucket = "tfstate.eu-west-2.artem" key = "${path_relative_to_include()}/terraform.tfstate" region = "eu-west-2" encrypt = true dynamodb_table = "my-lock-table" } }
Set the S3 bucket name that will store Terragrunt’s state, you don’t need to create it first, Terragrunt will create it yourself. We also set the region.
Create a file to store Terraform and Terragrunt variables:
touch common.tfvars variables.tf
We will write the first module that will create the VPC and Internet Gateway for it.
We create a directory for modules and for VPC in particular:
mkdir -p modules/vpc
Modules work on the principle of a function; when accessing them, we pass a number of variables.
To create a VPC, we need to know the following:
- VPC name
- CIDR
Create a separate file for the variables "modules/vpc/variables.tf" and add the following:
variable "vpc_name" {} variable "vpc_cidr" {}
We create the module itself, for this we create the file "modules/vpc/main.tf" with the following contents:
#################### CREATE VPC #################### resource "aws_vpc" "vpc" { cidr_block = var.vpc_cidr enable_dns_hostnames = "true" enable_dns_support = "true" tags = { Name = var.vpc_name } } ############# CREATE INTERNET GATEWAY ############## resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.vpc.id tags = { Name = "${var.vpc_name}-IGW" } }
We will create a template for three different environments:
- development
- staging
- production
Fill out the variable file for Terragrunt:
common.tfvars
### DEVELOPMENT ENVIRONMENT #### development_aws_region = "us-east-1" development_vpc_name = "ARTEM-DEVELOPMENT-USE1" development_vpc_cidr = "192.168.0.0/16" ######### END OF BLOCK ######### ##### STAGING ENVIRONMENT ###### staging_aws_region = "us-east-1" staging_vpc_name = "ARTEM-STAGING-USE1" staging_vpc_cidr = "192.168.0.0/16" ######### END OF BLOCK ######### #### PRODUCTION ENVIRONMENT #### production_aws_region = "eu-central-1" production_vpc_name = "ARTEM-PRODUCTION-EUC1" production_vpc_cidr = "192.168.0.0/16" ######### END OF BLOCK #########
Now fill in the Terraform variables file:
variables.tf
### DEVELOPMENT ENVIRONMENT #### variable "development_aws_region" {} variable "development_vpc_name" {} variable "development_vpc_cidr" {} ######### END OF BLOCK ######### ##### STAGING ENVIRONMENT ###### variable "staging_aws_region" {} variable "staging_vpc_name" {} variable "staging_vpc_cidr" {} ######### END OF BLOCK ######### #### PRODUCTION ENVIRONMENT #### variable "production_aws_region" {} variable "production_vpc_name" {} variable "production_vpc_cidr" {} ######### END OF BLOCK #########
We are ready to create the first environment, we will begin with the development of the environment
Create the structure:
mkdir -p environments/development
Let’s go to the created directory:
cd environments/development
Create the required file "terragrunt.hcl" and paste the following into it:
include { path = find_in_parent_folders() }
And create symbolic links to variable files located in the root:
ln -s ../../variables.tf variables.tf ln -s ../../common.tfvars common.tfvars
Create the file "main.tf" with the following contents:
terraform { backend "s3" {} } provider "aws" { region = var.development_aws_region }
That’s it, now you can create a Terraform file to call the module to create a VPC.
vpc.tf
module "vpc" { source = "../../modules/vpc" vpc_name = var.development_vpc_name vpc_cidr = var.development_vpc_cidr }
Run:
terragrunt init
At the first start, Terragrunt will say that the bucket for storing states does not exist, and will offer to create it, we agree.
After init, we look at the version of Terraform and the AWS provider, it is better to specify these versions in the "main.tf" file, if you plan to start from another computer (since we create a basket to store the state, we mean launch from any computer keeping current state)
terraform --version Terraform v0.12.9 + provider.aws v2.30.0
main.tf
terraform { backend "s3" {} required_version = "~> 0.12.9" } provider "aws" { region = var.development_aws_region version = "~> 2.30" }
Now, following the example of a development environment, we will create staging and production. Go back to the root of the project, delete the ".terraform" directory in "envarinments/development" and copy the development environment:
rm -rf environments/development/.terraform/ cp -aR environments/development environments/staging cp -aR environments/development environments/production
Change the variables for staging:
sed -i 's/development/staging/g' environments/staging/{main.tf,vpc.tf}
Change the variables for production:
sed -i 's/development/production/g' environments/production/{main.tf,vpc.tf}
Now, being at the root of the project, we can start planning for all environments with one command:
terragrunt plan-all
You can apply this template for each environment in particular, being in the directory of the necessary environment, run:
terragrunt apply
Or for all at once, being at the root of the project, run:
terragrunt apply-all
Our final structure is as follows:
. ├── common.tfvars ├── environments │ ├── development │ │ ├── common.tfvars -> ../../common.tfvars │ │ ├── main.tf │ │ ├── terragrunt.hcl │ │ ├── variables.tf -> ../../variables.tf │ │ └── vpc.tf │ ├── production │ │ ├── common.tfvars -> ../../common.tfvars │ │ ├── main.tf │ │ ├── terragrunt.hcl │ │ ├── variables.tf -> ../../variables.tf │ │ └── vpc.tf │ └── staging │ ├── common.tfvars -> ../../common.tfvars │ ├── main.tf │ ├── terragrunt.hcl │ ├── variables.tf -> ../../variables.tf │ └── vpc.tf ├── modules │ └── vpc │ ├── main.tf │ └── variables.tf ├── terragrunt.hcl └── variables.tf 6 directories, 20 files
In order to store our template in the repository, add the following to the ".gitignore" file:
.terraform/ terraform.*
This example can be downloaded from the Git repository:
https://bitbucket.org/artem-gatchenko/example-terraform-module-part1