Enhance Your IaC Strategy with Modular Terraform Configurations
Introduction
Terraform is a robust infrastructure as code (IaC) tool that empowers you to define and manage your infrastructure through code. By modularizing your Terraform configuration, you can break down your setup into reusable, manageable, and scalable components. This tutorial will walk you through creating a modularized Terraform setup, covering essential resources such as EC2 instances, S3 buckets, VPCs, and RDS instances
Project Structure
We'll start by defining our project structure. Here’s how our directory layout will look:
.
├── main.tf
├── terraform.tf
├── variables.tf
├── modules
│ ├── ec2
│ │ ├── main.tf
│ │ ├── variables.tf
│ ├── s3
│ │ ├── main.tf
│ │ ├── variables.tf
│ ├── vpc
│ │ ├── main.tf
│ │ ├── variables.tf
│ ├── rds
│ │ ├── main.tf
│ │ ├── variables.tf
Main Configuration Files
main.tf
The main.tf
file is where we define our primary Terraform configuration. This file calls the modules we have created for our infrastructure.
module "dev_infra" {
source = "./modules/ec2"
instance_count = 3
my_env = "dev"
instance_type = "t2.micro"
ami = "ami-09040d770ffe2224f"
}
module "prd_infra" {
source = "./modules/ec2"
instance_count = 3
my_env = "prd"
instance_type = "t2.medium"
ami = "ami-09040d770ffe2224f"
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "demo-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-2a"]
private_subnets = ["10.0.1.0/24"]
public_subnets = ["10.0.101.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = true
tags = {
Terraform = "true"
Environment = "dev"
}
}
module "rds" {
source = "./modules/rds"
instance_identifier = "my-rds-instance"
instance_class = "db.t2.micro"
engine = "mysql"
engine_version = "8.0"
username = "admin"
password = "password"
db_name = "mydb"
allocated_storage = 20
my_env = "dev"
}
dev_infra
andprd_infra
modules: These modules create EC2 instances for development and production environments.vpc
module: This module sets up a Virtual Private Cloud (VPC) using a publicly available VPC module.rds
module: This module creates an RDS instance.
terraform.tf
The terraform.tf
file configures the Terraform backend and provider.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.50.0"
}
}
backend "s3" {
bucket = "first-demo-state-bucket"
key = "terraform.tfstate"
region = "us-east-2"
dynamodb_table = "first-demo-state-table"
}
}
provider "aws" {
region = "us-east-2"
}
terraform
block: Specifies the required providers and backend configuration.provider
block: Configures the AWS provider.
variables.tf
The variables.tf
file defines the variables used in the main configuration.
variable "ami" {
type = string
description = "this will store ami for my instance"
}
variable "instance_count" {
type = number
description = "this is the number of instances you need"
}
variable "instance_type" {
type = string
description = "instance type"
}
variable "my_env" {
type = string
description = "this will store the env for my infra"
}
ami
: The Amazon Machine Image ID for the EC2 instances.instance_count
: The number of EC2 instances to create.instance_type
: The type of EC2 instances to create.my_env
: The environment (e.g., dev, prd).
Module Configuration Files
EC2 Module
resource "aws_instance" "my_demo_instance" {
count = var.instance_count
ami = var.ami
instance_type = var.instance_type
tags = {
Name = "${var.my_env}-instance"
env = var.my_env
}
}
This file defines an AWS EC2 instance resource. The aws_instance
block creates instances based on specified variables like AMI, instance type, and instance count. It also tags instances with the environment name. The count
parameter allows for creating multiple instances, while the tags
parameter helps in organizing and managing the instances by associating them with the environment they belong to. This modular approach makes it easy to deploy and manage EC2 instances in different environments by simply changing variable values.
modules/ec2/variables.tf
variable "ami" {
type = string
description = "this will store ami for my instance"
}
variable "instance_count" {
type = number
description = "this is the number of instances you need"
}
variable "instance_type" {
type = string
description = "instance type"
}
variable "my_env" {
type = string
description = "this will store the env for my infra"
}
This file declares variables used in the EC2 module. It includes variables for the AMI ID, instance count, instance type, and environment name, making the configuration flexible and reusable. By externalizing these parameters, the module can be reused across different environments and projects without modifying the main configuration file. This promotes consistency and reduces the potential for errors, as the same core logic is applied universally with environment-specific parameters.
S3 Module
resource "aws_s3_bucket" "my_bucket" { bucket = "${var.my_env}-first-terraform-app-bucket" tags = { Name = "${var.my_env}-first-terraform-app-bucket" } }
This file defines an AWS S3 bucket resource. The aws_s3_bucket
block creates a bucket with a name and tags based on the environment name, making it easy to identify and manage across different environments. Using the my_env
variable ensures that bucket names are unique and environment-specific, preventing conflicts and aiding in resource organization. This setup is particularly useful for managing multiple buckets across various stages of development, testing, and production environments.
modules/s3/variables.tf
variable "my_env" { type = string description = "this will store the env for my infra" }
This file declares a variable for the environment name, which is used to dynamically name and tag the S3 bucket, ensuring consistency across different environments. By leveraging a single variable, this module ensures that all related resources within an environment are consistently labeled, simplifying management and reducing the risk of misconfiguration. This approach enhances the clarity of resource allocation and usage across multiple environments.
VPC Module
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = var.name
cidr = var.cidr
azs = var.azs
private_subnets = var.private_subnets
public_subnets = var.public_subnets
enable_nat_gateway = var.enable_nat_gateway
enable_vpn_gateway = var.enable_vpn_gateway
tags = var.tags
}
This file uses the terraform-aws-modules/vpc/aws
module to create a VPC. It sets various parameters such as VPC name, CIDR block, availability zones, subnets, NAT gateway, and VPN gateway options, all configured through variables. This modular design simplifies the creation of a VPC by using a well-maintained external module. By specifying these parameters through variables, the VPC configuration can be easily adapted to different requirements and environments, ensuring a scalable and flexible network infrastructure.
modules/vpc/variables.tf
variable "name" {
type = string
}
variable "cidr" {
type = string
}
variable "azs" {
type = list(string)
}
variable "private_subnets" {
type = list(string)
}
variable "public_subnets" {
type = list(string)
}
variable "enable_nat_gateway" {
type = bool
}
variable "enable_vpn_gateway" {
type = bool
}
variable "tags" {
type = map(string)
}
This file declares variables for configuring the VPC module, including VPC name, CIDR block, availability zones, private and public subnets, NAT and VPN gateway options, and tags for resource identification. These variables allow for a highly customizable VPC setup that can accommodate a variety of network architectures. By using these variables, organizations can quickly replicate network configurations across multiple environments while maintaining control over key parameters.
RDS Module
resource "aws_db_instance" "rds_instance" {
instance_identifier = var.instance_identifier
instance_class = var.instance_class
engine = var.engine
engine_version = var.engine_version
username = var.username
password = var.password
db_name = var.db_name
allocated_storage = var.allocated_storage
tags = {
Name = "${var.my_env}-rds-instance"
env = var.my_env
}
}
This file defines an AWS RDS instance resource. The aws_db_instance
block creates an RDS instance with specified parameters like instance identifier, class, engine, engine version, username, password, database name, and allocated storage. It also tags the instance with the environment name. This configuration ensures that database instances are appropriately configured and tagged, facilitating easier management and monitoring. The use of variables allows for easy adjustments to database configurations as requirements change.
modules/rds/variables.tf
variable "instance_identifier" {
type = string
description = "The identifier for the RDS instance"
}
variable "instance_class" {
type = string
description = "The class of the RDS instance"
}
variable "engine" {
type = string
description = "The database engine to use"
}
variable "engine_version" {
type = string
description = "The version of the database engine"
}
variable "username" {
type = string
description = "The username for the RDS instance"
}
variable "password" {
type = string
description = "The password for the RDS instance"
}
variable "db_name" {
type = string
description = "The name of the database"
}
variable "allocated_storage" {
type = number
description = "The allocated storage for the RDS instance in GB"
}
variable "my_env" {
type = string
description = "This will store the environment for the RDS instance"
}
This file declares variables used in the RDS module, covering the instance identifier, class, engine, engine version, username, password, database name, allocated storage, and environment name. These variables allow for a flexible and customizable RDS instance configuration. By externalizing these parameters, this module can be reused to deploy RDS instances with different specifications across multiple environments, ensuring consistency and reducing the likelihood of configuration errors.
Conclusion
These modular Terraform configurations provide a structured approach to defining AWS resources. By using variables, they enable flexibility and reusability, allowing for easy customization and scaling of infrastructure components across different environments. This modular design promotes best practices in infrastructure as code (IaC), enabling teams to manage and provision resources efficiently and consistently. The use of modules also helps in maintaining a clean and organized codebase, making it easier to manage, review, and collaborate on infrastructure configurations. Overall, this approach streamlines the deployment process and enhances the scalability and maintainability of cloud infrastructure.