Demystifying Terraform Modules

Demystifying Terraform Modules

This article was originally written by "Kemane Donfack" on the blog: https://blog.numericaideas.com/terraform-modules

In the world of Infrastructure as Code (IaC), Terraform is a leading tool for managing and provisioning resources across various cloud providers. It empowers DevOps teams to define infrastructure in a declarative manner, ensuring consistent and reproducible deployments. Key to Terraform’s capabilities are modules, which play a pivotal role in creating scalable and maintainable infrastructure code.

FeaturedImage

Prerequisites

Before delving into Terraform modules, ensure you have the following:

  • Terraform Installation: Download the latest version from the official website.
  • AWS Account: Set up an appropriate AWS account credentials as documented here.
  • Basic HCL Knowledge: Familiarity with HashiCorp Configuration Language (HCL), used for writing Terraform configurations.
  • Modularity: Terraform allows the definition of reusable infrastructure modules, facilitating the creation of complex and scalable architectures and promoting best practice sharing.
  • Dependency Management: Terraform handles dependencies between resources, ensuring consistent updates and simplifying the management of interconnected infrastructures.

Introduction to Terraform Modules

What Are Terraform Modules?

Terraform modules are fundamental in this ecosystem. They are collections of Terraform configuration files, templates, and resources bundled into a single directory. Modules enable you to create reusable, composable, and shareable infrastructure components.

Why Use Terraform Modules?

Terraform modules offer several advantages:

  • Modularity: Modules break down your infrastructure code into manageable components, focusing on specific aspects. This simplifies understanding and maintenance.
  • Reusability: Modules can be shared across projects, encapsulating best practices for replicating infrastructure patterns.
  • Versioning: Modules can be versioned, ensuring consistent configurations throughout your infrastructure.

What is the Terraform Registry?

The Terraform Registry is a public repository maintained by HashiCorp, the creators of Terraform. It serves as a trusted source for sharing Terraform modules, provider plugins, and other resources. It’s the go-to platform for finding and sharing infrastructure code, promoting collaboration, and reducing the need to reinvent the wheel.

How to Create Terraform Modules?

Terraform Module Structure

Creating a Terraform module is straightforward. Structure it by organizing configuration files, variables, and outputs in a dedicated directory. Within this directory, include main.tf, outputs.tf, and variables.tf files to define resources, variables, and outputs specific to your module. Here’s an example folder structure for a Terraform project with a module:

my-terraform-project/
├── main.tf
├── variables.tf
├── outputs.tf
└── modules/
    └── my-module/
        ├── main.tf
        ├── variables.tf
        └── outputs.tf

Variables and Outputs in Modules

Variables parameterize your module. Define them in variables.tf to allow users to customize your module’s behavior in their Terraform configurations. Outputs, defined in outputs.tf, expose specific values for use in other Terraform configurations.

In variables.tf, define input variables like this:

variable "instance_count" {
  description = "Number of instances to create."
  type        = number
  default     = 1
}

In outputs.tf, define outputs like this:

output "instance_ips" {
  description = "Public IPs of the instances."
  value       = aws_instance.instances[*].public_ip
}

Using Terraform Modules

Modules in Configuration

Using a Terraform module in your configuration is straightforward. Specify the module source in your code, like this:

module "example" {
  source          = "./modules/my-module"
  instance_count  = 2
}

In this example, we use a module named example located in the ./modules/my-module directory. We also provide a value for the instance_count variable.

Now, let’s explore how we could have reused or installed that module if it were stored in the Terraform Registry or located in a GitHub repository.

Using Modules from the Terraform Registry

The Terraform Registry is a public repository of Terraform modules provided by HashiCorp and the Terraform community. To use a module from the Terraform Registry, you would specify the source as follows:

module "example" {
  source = "terraform-aws-modules/ec2-instance/aws"
  instance_count = 2
}

Using Modules from GitHub

If the module is hosted on GitHub, you can reference it using the git:: source syntax as demonstrated below:

module "example" {
  source = "git::https://github.com/your-username/your-module.git"
  instance_count = 2
}

Here, https://github.com/your-username/your-module.git should be replaced with the actual URL of your GitHub repository containing the module.

By specifying the appropriate source, you can easily reuse or install modules from various sources, including the Terraform Registry and GitHub, making your infrastructure management more efficient and flexible.

Terraform Module Examples

To grasp the power of Terraform modules, let’s dive into real-world examples. We’ll create two modules: one for provisioning an EC2 instance and another for creating a Security Group that we’ll associate with the instance.

The Security Group Module

The Security Group module encapsulates the creation of an AWS Security Group, acting as a virtual firewall for EC2 instances, controlling inbound and outbound traffic.

Module Structure

Directory structure for our Security Group module:

my-terraform-project/
└── modules/
    └── security-group/
        ├── main.tf
        ├── variables.tf
        ├── outputs.tf

Variables

In variables.tf, define variables for customizing the Security Group:

variable "security_group_name" {
  description = "Name of the security group"
}

variable "security_group_description" {
  description = "Description of the security group"
}

variable "inbound_port" {
  type        = list(any)
  description = "List of inbound ports to allow"
}

variable "vpc_id" {
  description = "ID of the VPC"
}

Resources

In main.tf, create the Security Group:

# Create a security group
resource "aws_security_group" "security" {
  name        = var.security_group_name
  description = var.security_group_description
  vpc_id      = var.vpc_id

  dynamic "ingress" {
    for_each = var.inbound_port
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = -1
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Outputs

Our Security Group module exposes the Security Group’s ID as an output in outputs.tf:

output "security_group_id" {
  value = aws_security_group.security.id
}

The EC2 Instance Module

The EC2 instance module creates an EC2 instance and associates it with the Security Group created using our Security Group module, ensuring instances launch with the necessary security configurations.

Module Structure

Directory structure for our EC2 instance module:

my-terraform-project/
└── modules/
    └── ec2-instance/
        ├── main.tf
        ├── variables.tf
        └── outputs.tf

Variables

In variables.tf, define variables to customize the EC2 instance:

variable "instance_name" {
  description = "Name of the EC2 instance launched with this configuration"
}

variable "aws_subnet_id" {
  description = "ID of the AWS subnet for launching the EC2 instance"
}

variable "ami" {
  description = "ID of the Amazon Machine Image (AMI) for the EC2 instance"
}

variable "key_name" {
  description = "Name of the SSH key pair for connecting to the EC2 instance"
}

variable "instance_type" {
  description = "Type of EC2 instance to launch (e.g., t2.micro, m5.large, etc.)"
}

variable "ebs_volume_size" {
  description = "Size (in GB) of the EBS volume to attach to the EC2 instance"
}

variable "ec2_sg_id" {
  description = "ID of the EC2 Security Group(s) to associate with the EC2 instance"
}

Resources

In main.tf,

create the EC2 instance:

resource "aws_instance" "instance" {
  ami                    = var.ami
  instance_type          = var.instance_type
  subnet_id              = var.aws_subnet_id
  vpc_security_group_ids = var.ec2_sg_id
  key_name               = var.key_name

  root_block_device {
    volume_size           = var.ebs_volume_size
    volume_type           = "gp3"
    delete_on_termination = true
  }

  tags = {
    Name = var.instance_name
  }
}

Outputs

Our EC2 instance module exposes the public IP address of the instance as an output in outputs.tf

output "instance_ip" {
  value = aws_instance.instance.public_ip
}

Using the Modules

Now that we have created our Security Group and EC2 Instance modules, let’s explore how to use them in a Terraform configuration.

In your Terraform configuration, use these modules as shown below:

data "aws_vpc" "myvpc" {
  id = "vpc-xxxxxx"
}

data "aws_subnet" "mysubnet" {
  id = "subnet-xxxxxx"
}

module "security_group_ec2" {
  source                     = "./modules/security-group"
  security_group_name        = "myec2-sg"
  security_group_description = "EC2 Security Group"
  inbound_port               = [8080, 22]
  vpc_id                     = data.aws_vpc.myvpc.id
}

module "myinstance" {
  source                    = "./modules/ec2-instance"
  aws_subnet_id             = data.aws_subnet.mysubnet.id
  instance_name             = "EC2"
  ami                       = "ami-08766f81ab52792ce"
  key_name                  = "mykey"
  instance_type             = "t3.micro"
  ebs_volume_size           = 30
  ec2_sg_id                 = [module.security_group_ec2.security_group_id]
}

In this example:

  • Define data sources for the VPC and Subnet where your EC2 instance will be associated.
  • Use the module block to create an instance of our Security Group module named security_group_ec2. Specify parameters such as security_group_name, security_group_description, inbound_port, and vpc_id to customize the Security Group.
  • Provision an EC2 instance using the myinstance module. Provide parameters like aws_subnet_id, instance_name, ami, key_name, instance_type, ebs_volume_size, and ec2_sg_id. The ec2_sg_id parameter is set to the security_group_id output of the security_group_ec2 module, ensuring the EC2 instance associates with the correct Security Group.

By adopting these modules and using this configuration, efficiently manage the security and provisioning of EC2 instances while ensuring consistency and security best practices.

To deploy your infrastructure, execute the following commands:

terraform init
terraform plan
terraform apply -auto-approve

Once completed, your infrastructure should resemble the image displayed below:

Infrastructure

The complete source code of the project is available on GitHub.

In order to better your DevOps skills, learn How To Deploy WordPress on a 2-Tier AWS Architecture using Terraform by following this practical workshop:

Conclusion

Terraform modules are fundamental for creating maintainable and scalable Infrastructure as Code. They allow encapsulation of infrastructure components, promote reusability, and simplify the management of complex resources. Mastering Terraform modules enhances your ability to provision and manage infrastructure efficiently, making you a more effective DevOps engineer.

Leave a Reply

Your email address will not be published. Required fields are marked *

Press ESC to close