🌐🔥TerraWeek Challenge 🔥🌐 Day5 : Terraform Modules

·

7 min read

🌐🔥TerraWeek Challenge 🔥🌐

Day5 :  Terraform Modules

📍 Introduction

🐧Welcome to the Day 5 of our blog series on Terraform! In this post, we'll describe you to Terraform Modules. By the end of this day, you’ll gain a deeper understanding of why do we need modules in Terraform and the benefits of using modules in Terraform🐧

👨🏻‍💻Task🛠️🚧👨‍💻

📚Task 1: (a)What are modules in Terraform and why do we need modules in Terraform?

🎯Suppose there is a piece of code that needs to be reused (for the provisioning of resources). We will create a module( for reusable code) apart from the root module/parent module & give a reference of the child module (template of reusable code) in the root module so that the created child module will be used again & again.

🎯A Terraform module is a collection of standard configuration files in a dedicated directory. Terraform modules encapsulate groups of resources dedicated to one task, reducing the amount of code you have to develop for similar infrastructure components.

🎯Some say that Terraform modules are a way of extending your present Terraform configuration with existing parts of reusable code reducing the amount of code you have to develop for similar infrastructure components. Others say that the Terraform module definition is a single or many .tf files stacked together in their own directory. Both are correct.

🎯Module blocks can also be used to force compliance on other resources—to deploy databases with encrypted disks, for example. By hard-coding the encryption configuration and not exposing it through variables, you’re making sure that every time the module is used, the disks are going to be encrypted.

A typical module can look like this:

.
├── main.tf
├── outputs.tf
├── README.md
└── variables.tf

(b) :- What are the benefits of using modules in Terraform?

Using modules in Terraform provides several benefits:

Reusability: Modules allow you to encapsulate and package reusable pieces of infrastructure code. You can create modules for common infrastructure patterns or components, such as a web server, database, load balancer, or VPC. Once you've defined a module, you can reuse it across multiple projects, reducing duplication and promoting consistency.

If you want to make VPC (Virtual Private Cloud) on AWS then what do you need to make VPC?

These CIDR, Subnet, Internet Gateway, and NAT Gateway are already written in Modules.

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = "my-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  enable_nat_gateway = true
  enable_vpn_gateway = true

  tags = {
    Terraform = "true"
    Environment = "dev"
  }
}

Usage :- You have to use this code and it will create a VPC for you. It will create one VPC, one CIDR, one subnet, Public subnet, NAT gateway, vpn_gateway or internet gateway. So basically you can create all these things by this code.
And this thing is called Modules.

AWS has a CIDR block, subnet block, and these resources. You just have to use it, those AWS modules. so you don't have to re-write everything. You can re-use that.

📚Task 2 :- Create/Define a module in Terraform to encapsulate reusable infrastructure configuration in a modular and scalable manner. For e.g. EC2 instance in AWS, Resource Group in Azure, Cloud Storage bucket in GCP.

🎯In Terraform, a module is a reusable unit of infrastructure configuration that allows you to encapsulate related resources, their dependencies, and configurations in a modular and scalable manner.

🎯Creating an EC2 instance in AWS using a Terraform module. First, you would define the module itself, which includes the necessary inputs and outputs.

  # main.tf

  # Define the module
  module "ec2_instance" {
    source     = "./modules/ec2_instance"
    instance_type = "t2.micro"
    ami_id     = "ami-0123456789abcdef0"
    subnet_id  = "subnet-0123456789abcdef0"
  }

In this example, we have defined a module called "ec2_instance" that is sourcing its configuration from a local directory "./modules/ec2_instance". It takes three inputs: instance_type, ami_id, and subnet_id. These inputs can be customized when using the module.

Next, let's look at the module itself :-

  # modules/ec2_instance/main.tf

  # Create the EC2 instance
  resource "aws_instance" "example" {
    ami           = var.ami_id
    instance_type = var.instance_type
    subnet_id     = var.subnet_id

    # Other configuration options for the EC2 instance...
  }

  # Output the instance ID
  output "instance_id" {
    value = aws_instance.example.id
  }

In this module, we define an AWS EC2 instance resource using the aws_instance block. The values for ami, instance_type, and subnet_id are provided through variables (var.ami_id, var.instance_type, and var.subnet_id) that will be set when using the module.

🎯Additionally, we define an output called "instance_id" which captures the id of the created EC2 instance.

With this module in place, you can reuse it in multiple places within your Terraform code. For example, you can instantiate multiple instances with different configurations by using the module block multiple times and providing different values for the inputs.

🎯By using modules, you can encapsulate complex infrastructure configurations, promote code reuse, and achieve scalability and maintainability in your infrastructure-as-code projects.

📚Task 3 :- Dig into modular composition and module versioning.

🎯Modular Composition:

Modular composition in Terraform refers to the practice of breaking down your infrastructure code into smaller, reusable, and maintainable modules. Modules are self-contained units of Terraform configurations that can be used to define specific pieces of infrastructure. By using modules, you can abstract away complexity, promote code reuse, and make your Terraform configurations more modular and organized.

🎯Module Versioning:

Module versioning is the practice of assigning specific versions to your Terraform modules to ensure consistency and stability across your infrastructure code. This is particularly important when you have multiple teams or projects using the same modules or when you need to ensure that changes to modules don’t break existing infrastructure.

Suppose you have the following directory structure:

my_aws_project/
|-- main.tf
|-- variables.tf
|-- outputs.tf
|-- modules/
|   |-- s3_bucket/
|       |-- main.tf
|       |-- variables.tf
|       |-- outputs.tf

Create the Local Module:

In the modules/s3_bucket directory, create the Terraform files for your local module:

main.tf (defines the AWS S3 bucket):

resource "aws_s3_bucket" "example" {
  bucket = var.bucket_name
  acl    = "private"
}

variables.tf (defines input variables for the module):

variable "bucket_name" {
  description = "The name of the S3 bucket"
}

outputs.tf (defines outputs for the module):

output "bucket_id" {
  description = "The ID of the created S3 bucket"
  value       = aws_s3_bucket.example.id
}

Version Your Local Module :-

In the modules/s3_bucket directory, create a version.txt file and specify the initial version for your local module. Let's set it to version 1.0.0:

1.0.0

Reference the Local Module in the Root Configuration:

In your root module directory (where main.tf, variables.tf, and outputs.tf are located), create or update your main.tf to reference the local module with a version constraint:

terraform {
  required_providers {
    aws = ">= 2.0, < 4.0"
  }
}

module "s3_example" {
  source  = "./modules/s3_bucket"
  version = ">= 1.0.0, < 2.0.0"
  bucket_name = "my-unique-bucket-name"
}

# Use the output from the module
output "s3_bucket_id" {
  value = module.s3_example.bucket_id
}

📚Task 4 :- What are the ways to lock Terraform module versions? Explain with code snippets.

🎯Locking Terraform module versions is important to ensure that your infrastructure remains stable and predictable as you make changes to your configurations or modules. Terraform provides several ways to lock module versions. Here are three common methods:

  1. Version Constraints in Root Configuration :- You can specify version constraints for your modules directly in your root configuration (e.g., main.tf). This allows you to control which version of a module to use. Here's an example:
terraform {
  required_providers {
    aws = ">= 2.0, < 4.0"
  }
}

module "example" {
  source  = "namespace/module_name/aws"
  version = "1.2.0"  # Specific version constraint
  variable1 = "value1"
  variable2 = "value2"
}

In this example, the version attribute within the module block specifies the desired version constraint for the module. You can use exact versions, ranges, or other version constraints to control module versions.

2. required_version in versions.tf: You can create a versions.tf file in your root module directory and specify the required_version there. This locks the Terraform version itself. For example:

# versions.tf
terraform {
  required_version = ">= 0.14, < 0.15"
}

By specifying required_version, you ensure that only Terraform versions within the specified range can be used to apply the configuration. This helps prevent unintended upgrades that could introduce breaking changes.

3. Use a .lock File: Terraform can generate a .terraform.lock.hcl file when you run terraform init. This file captures the resolved versions of providers and modules, effectively locking them to the specific versions used in your environment. You should commit this file to version control to ensure that all team members use the same module versions.

Here’s an example of a .terraform.lock.hcl file:

# .terraform.lock.hcl
provider "aws" {
  version = "3.48.0"
  hash    = "h1:abcdefg1234567..."
}

module "example" {
  source  = "namespace/module_name/aws"
  version = "1.2.0"
  hash    = "h1:1234567890abcdef..."
}

🎯You can regenerate this file with the terraform init command whenever you update your configurations. Team members can then use the same .lock file to ensure consistent module versions.

Thank you for reading! I hope you find this article helpful

❄❄❄🐋🐋❄❄❄The End❄❄❄🐋🐋❄❄❄