Video Thumbnail for Lesson
6.2: Terraform Modules (Applied)

Using Terraform Modules

In this lesson, we will demonstrate how to refactor our example web application configuration using Terraform modules. We will also show how to consume a third-party module.

Consuming a third-party module:

We will start by deploying the Consul module. Consul is a tool by HashiCorp for automating network setup and discovery.

The module is available via the Terraform Registry and helps configure the system with best practices while exposing key input variables.

To deploy the Consul module, navigate to the Consul subdirectory, and create a main.tf file. Set up the back-end and providers, then reference the Consul module via the GitHub repo where it is stored.

terraform {
  # Assumes s3 bucket and dynamo DB table already set up
  # See /code/03-basics/aws-backend
  backend "s3" {
    bucket         = "devops-directive-tf-state"
    key            = "06-organization-and-modules/consul/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-state-locking"
    encrypt        = true
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

############################################################
##
## NOTE: if you are deploying this in your production setup
## follow the instructions in the github repo on how to modify
## deploying with the defaults here as an example of the power
## of modules.
##
## REPO: https://github.com/hashicorp/terraform-aws-consul
##
############################################################
module "consul" {
  source = "git@github.com:hashicorp/terraform-aws-consul.git"
}

Run terraform init and terraform plan to see that it would provision 52 different resources within the AWS account. These resources include EC2 instances, networking policies, and IAM roles.

By using the Consul module, we can easily configure the complex system without having to manually deploy each resource.

Refactoring the web application configuration:

Now let's refactor our example web application configuration using Terraform modules.

To do this, we will break up the configuration into different components:

  • compute
  • database
  • storage
  • networking.

Move the resource definitions from the main.tf file to their corresponding .tf files.

āÆ tree ./06-organization-and-modules
.
ā”œā”€ā”€ README.md
ā”œā”€ā”€ web-app
ā”‚Ā Ā  ā””ā”€ā”€ main.tf
ā””ā”€ā”€ web-app-module
    ā”œā”€ā”€ compute.tf
    ā”œā”€ā”€ database.tf
    ā”œā”€ā”€ dns.tf
    ā”œā”€ā”€ main.tf
    ā”œā”€ā”€ networking.tf
    ā”œā”€ā”€ outputs.tf
    ā”œā”€ā”€ storage.tf
    ā””ā”€ā”€ variables.tf

3 directories, 12 files

To consume the module:

  1. Create a main.tf file in the web-app directory to consume the web-application-module module
  2. Set up the back-end and required providers
  3. Define the input variables that the module will consume
terraform {
  # Assumes s3 bucket and dynamo DB table already set up
  # See /code/03-basics/aws-backend
  backend "s3" {
    bucket         = "devops-directive-tf-state"
    key            = "06-organization-and-modules/web-app/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-state-locking"
    encrypt        = true
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

variable "db_pass_1" {
  description = "password for database #1"
  type        = string
  sensitive   = true
}

variable "db_pass_2" {
  description = "password for database #2"
  type        = string
  sensitive   = true
}

module "web_app_1" {
  source = "../web-app-module"

  # Input Variables
  bucket_prefix    = "web-app-1-data"
  domain           = "devopsdeployed.com"
  app_name         = "web-app-1"
  environment_name = "production"
  instance_type    = "t2.micro"
  create_dns_zone  = true
  db_name          = "webapp1db"
  db_user          = "foo"
  db_pass          = var.db_pass_1
}

module "web_app_2" {
  source = "../web-app-module"

  # Input Variables
  bucket_prefix    = "web-app-2-data"
  domain           = "anotherdevopsdeployed.com"
  app_name         = "web-app-2"
  environment_name = "production"
  instance_type    = "t2.micro"
  create_dns_zone  = true
  db_name          = "webapp2db"
  db_user          = "bar"
  db_pass          = var.db_pass_2
}

By using these two module blocks, we can easily deploy two copies of the web application with slightly different configurations.

When running terraform apply, you will be prompted to enter the values for db_pass_1 and db_pass_2. Once the apply is complete, you will have two copies of the web application running across four EC2 instances in AWS.

Finally, run terraform destroy to clean up the resources created during this lesson.

Now that you have seen how to refactor your Terraform code into different sections, parameterize it with variables, and abstract complexity using modules, you can efficiently provision multiple instances of the web application with different configurations.