Table of Contents
Introduction
Blogging about AWS Terraform modules goes beyond a simple exploration; it's about sharing insights into a transformative technology. As we navigate through the essentials, you'll discover the power of Infrastructure as Code (IaC) and how Terraform simplifies the deployment and management of resources on AWS.
Understanding AWS Terraform Module Basics
What are AWS Terraform modules?
AWS Terraform modules are encapsulated, reusable components that define a set of resources with a specific purpose. Whether it's setting up a VPC, launching EC2 instances, or configuring security groups, modules provide a modular and efficient approach to infrastructure management.
Key components and structure
A deep dive into the anatomy of Terraform modules, exploring variables, outputs, and the hierarchical structure that makes them versatile and adaptable.
Benefits of using modules in AWS Terraform
Unlock the advantages of modularization, including code reusability, easier maintenance, and scalability. Learn how modules enhance collaboration and accelerate development workflows.
Setting Up AWS Terraform: A Step-by-Step Guide
Configuring AWS credentials
Navigate the AWS authentication process within Terraform, ensuring a secure connection to your AWS environment.
$ aws configure
VPC and EC2 Instances
The foundation of our infrastructure lies within the Virtual Private Cloud (VPC), providing network isolation and segmentation. Coupled with EC2 instances, our compute resources are configured with precision, enabling the seamless deployment and scaling of applications.
S3 Buckets
Leveraging Amazon S3, we established scalable and durable object storage for our data. Whether hosting static assets, serving as a data lake, or facilitating backups, S3 ensures reliability and accessibility.
CloudFront for Global Content Delivery
The integration of AWS CloudFront significantly boosts the performance and global reach of our applications. The CDN distributes content to edge locations worldwide, reducing latency and enhancing the overall user experience.
Route 53 for DNS Management
With AWS Route 53, our DNS management is streamlined and highly available. Whether directing traffic to our CloudFront distributions or EC2 instances, Route 53 ensures that our applications are easily discoverable on the internet.
Certificate Manager for Secure Communication
Security is paramount, and AWS Certificate Manager allows us to secure our communication channels with SSL/TLS certificates. This ensures encrypted connections and establishes trust with our users.
Terraform as the Orchestrator
The true hero of our infrastructure orchestration is Terraform. With its declarative syntax, we've defined and managed the entire AWS environment in code. Terraform enables version-controlled infrastructure changes, simplifying deployment, and fostering collaboration.
Creating a simple Terraform script
Hands-on guidance for crafting a basic Terraform script, providing a foundation for more complex configurations.
devops@techvoot:~/Documents/terraform-projects/aws/module-aws$ ls
|- main.tf
|- variables.tf
|- modules
|- ec2
|- main.tf modules
|- outputs.tf
|- variables.tf
|- vpc
|- main.tf modules
|- outputs.tf
|- variables.tf
|- rds
|- main.tf modules
|- outputs.tf
|- variables.tf
|- s3
|- main.tf modules
|- outputs.tf
|- variables.tf
|- route53
|- main.tf modules
|- outputs.tf
|- variables.tf
|- certmanager
|- main.tf modules
|- outputs.tf
|- variables.tf
root directory files
devops@techvoot:~/Documents/terraform-projects/aws/module-aws$ cat main.tf
# configure aws provider
provider "aws" {
region = var.region
}
module "vpc" {
source = "./modules/vpc"
region = var.region
vpc_name = var.vpc_name
vpc_cidr = var.vpc_cidr
public_subnet_az1_cidr = var.public_subnet_az1_cidr
public_subnet_az2_cidr = var.public_subnet_az2_cidr
private_subnet_az1_cidr = var.private_subnet_az1_cidr
private_subnet_az2_cidr = var.private_subnet_az2_cidr
security_group_name = var.security_group_name
}
# create EC2
module "ec2" {
source = "./modules/ec2"
subnet_id = module.vpc.public_subnet_az1
ec2_ami = var.ec2_ami
ec2_type = var.ec2_type
ec2_security_group_id = module.vpc.ec2_security_group_id
ec2_name = var.ec2_name
key_name = var.key_name
}
#Create RDS
module "rds" {
source = "./modules/rds"
subnet_id_az1 = module.vpc.public_subnet_az1
subnet_id_az2 = module.vpc.public_subnet_az2
allocated_storage = var.allocated_storage
engine = var.db_engine
engine_version = var.engine_version
instance_class = var.instance_class
db_name = var.db_name
username = var.username
password = var.password
db_subnet_group_name = var.db_subnet_group_name
subnet_group_tag = var.subnet_group_tag
skip_final_snapshot = var.skip_final_snapshot
}
# Route 53
module "route53" {
source ="./modules/route53"
domain_name = var.domain_name
}
#S3 bucket
module "s3" {
source ="./modules/s3"
bucket_name = var.bucket_name
bucket_tag = var.bucket_tag
}
# Cert Manager
module "certmanager" {
source ="./modules/certmanager"
wild_domain_name = var.wild_domain_name
w_domain_name = var.w_domain_name
cert_managertag = var.cert_managertag
hosted_zone_id = module.route53.hosted_zone_id
}
### CloudFront
module "cloudfront" {
source ="./modules/cloudfront"
bucket_domain_name = module.s3.bucket_domain_name
viewer_certificate_arn = module.certmanager.aws_acm_certificate_arn
depends_on = [
module.certmanager
]
}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws$ cat variable.tf
#Create VPC
variable "region" {
type = string
default = "ap-south-1"
}
variable "vpc_name" {
type = string
default = "demo"
}
variable "vpc_cidr" {
type = string
default = "10.0.0.0/16"
}
variable "public_subnet_az1_cidr" {
type = string
default = "10.0.1.0/24"
}
variable "public_subnet_az2_cidr" {
type = string
default = "10.0.2.0/24"
}
variable "private_subnet_az1_cidr" {
type = string
default = "10.0.10.0/24"
}
variable "private_subnet_az2_cidr" {
type = string
default = "10.0.11.0/24"
}
# Security Group for vpc
variable "security_group_name" {
type = string
default = "mysg0001"
}
# EC2
variable "ec2_ami" {
type = string
default = "ami-0a7cf821b91bcccbc"
}
variable "ec2_type" {
default = "t2.micro"
}
variable "ec2_name" {
type = string
default = "myec2000"
}
variable "key_name" {
description = "SSH keys to connect to ec2 instance"
default = "your aws created key.pem"
}
# Create RDS
variable "allocated_storage" {
type = string
default = "20"
}
variable "db_engine" {
type = string
default = "mysql"
}
variable "engine_version" {
type = string
default = "8.0.28"
}
variable "instance_class" {
type = string
default = "db.t2.micro"
}
variable "db_name" {
type = string
default = "my_RDS_database"
}
variable "username" {
type = string
default = "*******"
}
variable "password" {
type = string
default = "**********"
}
variable "skip_final_snapshot" {
type = bool
default = "true"
}
variable "db_subnet_group_name" {
type = string
default = "rds_database_subnet"
}
variable "subnet_group_tag" {
type = string
default = "rds_databse_saabnet"
}
#S3 Bucket
variable "bucket_name" {
type = string
default = "****.com"
}
variable "bucket_tag" {
type = string
default = "****.com"
}
# Cert Manager
variable "wild_domain_name" {
type = string
default = "*(****.com)’
}
variable "w_domain_name" {
type = string
default = "www.****.com"
}
variable "cert_managertag" {
type = string
default = "****.com-certmanager"
}
/* variable "type" {
type = string
default = "A"
} */
# Route 53
variable "domain_name" {
type = string
default = "****.com "
}
module-ec2
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/ec2$ cat main.tf
resource "aws_instance" "web" {
ami = var.ec2_ami
instance_type = var.ec2_type
subnet_id = var.subnet_id
key_name = var.key_name
vpc_security_group_ids = [var.ec2_security_group_id]
#key_name= var.key_name
tags = {
Name = var.ec2_name
}
}
resource "aws_eip" "lb" {
instance = aws_instance.web.id
vpc = true
}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/ec2$ cat variables.tf
variable "ec2_ami" {}
variable "ec2_type" {}
variable "subnet_id" {}
variable "ec2_name" {}
variable "ec2_security_group_id" {}
variable "key_name" {}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/ec2$ cat outputs.tf
output "ec2_id" {
value = aws_instance.web.id
}
output "ec2_pub_ip" {
value = aws_instance.web.public_ip
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/ec2$ cat user-data.sh
#!/bin/bash
# Update system packages
sudo apt-get update -y
# Install developer dependencies
sudo apt-get install build-essential -y
# Update system packages
sudo apt-get install nginx -y
sudo systemctl start nginx.service
sudo systemctl enable nginx.service
# Create project directory
sudo mkdir /var/sites
sudo chown -R ubuntu:ubuntu /var/sites
echo "softwares installed successfully.."
module-vpc
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/vpc$ cat main.tf
resource "aws_vpc" "vpc" {
cidr_block = var.vpc_cidr
instance_tenancy = "default"
enable_dns_hostnames = true
tags = {
Name = "${var.vpc_name}-vpc"
}
}
# create internet gateway and attach it to vpc
resource "aws_internet_gateway" "internet_gateway" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.vpc_name}-igw"
}
}
# use data source to get all availability zones in region
data "aws_availability_zones" "available_zones" {}
# create public subnet az1
resource "aws_subnet" "public_subnet_az1" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.public_subnet_az1_cidr
availability_zone = data.aws_availability_zones.available_zones.names[0]
map_public_ip_on_launch = true
tags = {
Name = "${var.vpc_name}-pub_subnet_az1"
}
}
# create route table and add public route
resource "aws_route_table" "public_route_table" {
vpc_id = aws_vpc.vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.internet_gateway.id
}
tags = {
Name = "${var.vpc_name}-public route table"
}
}
# associate public subnet az1 to "public route table"
resource "aws_route_table_association" "public_subnet_az1_route_table_association" {
subnet_id = aws_subnet.public_subnet_az1.id
route_table_id = aws_route_table.public_route_table.id
}
# create private app subnet az1
resource "aws_subnet" "private_app_subnet_az1" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.private_subnet_az1_cidr
availability_zone = data.aws_availability_zones.available_zones.names[0]
map_public_ip_on_launch = false
tags = {
Name = "${var.vpc_name}-private subnet az1"
}
}
#Elastic ip
resource "aws_eip" "nateIP" {
vpc = true
}
#NAT Gateway for VPC
resource "aws_nat_gateway" "natgw" {
allocation_id = aws_eip.nateIP.id
subnet_id = aws_subnet.public_subnet_az1.id
tags = {
Name = "${var.vpc_name}-NAT Gateway"
}
}
# create route table and add Private route
resource "aws_route_table" "private_route_table" {
vpc_id = aws_vpc.vpc.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.natgw.id
}
tags = {
Name = "${var.vpc_name}-private route table"
}
}
# associat Private subnet az1 to "private route table"
resource "aws_route_table_association" "private_subnet_az1_route_table_association" {
subnet_id = aws_subnet.private_app_subnet_az1.id
route_table_id = aws_route_table.private_route_table.id
}
# create security group for EC2
resource "aws_security_group" "ec2_security_group" {
name = "ec2 security group"
description = "enable http/https access on port 80/443 & dynamodb 3306"
vpc_id = aws_vpc.vpc.id
ingress {
description = "http access"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "https access"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "MYSQL access"
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "SSH"
from_port = 22
to_port = 22
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"]
}
tags = {
Name = var.security_group_name
}
depends_on = [
aws_vpc.vpc
]
}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/vpc$ cat variables.tf
variable "region" {}
variable "vpc_name" {}
variable "vpc_cidr" {}
variable "public_subnet_az1_cidr" {}
variable "private_subnet_az1_cidr" {}
variable "security_group_name" {}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/vpc$ cat outputs.tf
output "vpc_id" {
value = aws_vpc.vpc.id
}
output "public_subnet_az1" {
value = aws_subnet.public_subnet_az1.id
}
output "private_subnet_az1" {
value = aws_subnet.private_app_subnet_az1.id
}
output "ec2_security_group_id" {
value = aws_security_group.ec2_security_group.id
}
module-s3
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/s3$ cat main.tf
resource "aws_s3_bucket" "b" {
bucket = var.bucket_name
tags = {
Name = var.bucket_tag
}
}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/s3$ cat variables.tf
variable "bucket_name" {}
variable "bucket_tag"{}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/s3$ cat outputs.tf
output "bucket_domain_name" {
value = aws_s3_bucket.b.bucket_regional_domain_name
}
module-rds
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/rds$ cat main.tf
# terraform aws db subnet group
resource "aws_db_subnet_group" "database-subnet-group" {
name = var.db_subnet_group_name
subnet_ids = [var.subnet_id_az1]
tags = {
Name = var.subnet_group_tag
}
}
resource "aws_db_instance" "rds" {
allocated_storage = var.allocated_storage
engine = var.engine
engine_version = var.engine_version
instance_class = var.instance_class
db_name = var.db_name
username = var.username
password = var.password
skip_final_snapshot = var.skip_final_snapshot
}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/rds$ cat variables.tf
#variable "subnet_id" {}
variable "db_subnet_group_name" {}
variable "subnet_id_az1" {}
variable "subnet_group_tag" {}
variable "allocated_storage" {}
variable "engine" {}
variable "engine_version" {}
variable "instance_class" {}
variable "db_name" {}
variable "username" {}
variable "password" {}
variable "skip_final_snapshot" {}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/rds$ cat outputs.tf
output "aws_db_instance_id" {
value = aws_db_instance.rds.id
}
module-cloudfront
Documents/terraform-projects/aws/module-aws/modules/cloudfront/$cat main.tf
resource "aws_cloudfront_origin_access_identity" "oai" {
comment = var.bucket_domain_name
}
locals {
s3_origin_id = var.bucket_domain_name
}
resource "aws_cloudfront_distribution" "s3_distribution" {
origin {
domain_name = var.bucket_domain_name
origin_id = local.s3_origin_id
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.oai.cloudfront_access_identity_path
}
}
enabled = true
is_ipv6_enabled = true
comment = "my distribution"
default_root_object = "index.html"
logging_config {
include_cookies = false
bucket = var.bucket_domain_name
prefix = "myprefix"
}
# aliases = ["mysite.example.com", "yoursite.example.com"]
default_cache_behavior {
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = local.s3_origin_id
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "allow-all"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
# Cache behavior with precedence 0
ordered_cache_behavior {
path_pattern = "/content/immutable/*"
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD", "OPTIONS"]
target_origin_id = local.s3_origin_id
forwarded_values {
query_string = false
headers = ["Origin"]
cookies {
forward = "none"
}
}
min_ttl = 0
default_ttl = 86400
max_ttl = 31536000
compress = true
viewer_protocol_policy = "redirect-to-https"
}
# Cache behavior with precedence 1
ordered_cache_behavior {
path_pattern = "/content/*"
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = local.s3_origin_id
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
compress = true
viewer_protocol_policy = "redirect-to-https"
}
price_class = "PriceClass_200"
restrictions {
geo_restriction {
restriction_type = "whitelist"
locations = ["US", "CA", "GB", "DE"]
}
}
tags = {
Environment = "production"
}
viewer_certificate {
acm_certificate_arn = var.viewer_certificate_arn
ssl_support_method = "sni-only"
}
}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/cloudfront$ cat variable.tf
variable "bucket_domain_name" {}
variable "viewer_certificate_arn"{}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/cloudfront$ cat output.tf
output "cloudfront_distribution_id" {
value = aws_cloudfront_distribution.s3_distribution.id
}
output "cloudfront_domain_name" {
value = aws_cloudfront_distribution.s3_distribution.domain_name
}
output "cloudfront_distribution_url" {
value = aws_cloudfront_distribution.s3_distribution.domain_name
}
output "cloudfront_distribution_arn" {
value = aws_cloudfront_distribution.s3_distribution.arn
}
output "cloudfront_oai_id" {
value = aws_cloudfront_origin_access_identity.oai.id
}
output "cloudfront_oai_s3_canonical_user_id" {
value = aws_cloudfront_origin_access_identity.oai.s3_canonical_user_id
}
module-route53
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/route53$ cat main.tf
resource "aws_route53_zone" "primary" {
name = var.domain_name
}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/route53$ cat variable.tf
variable "domain_name" {}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/route53$ cat output.tf
output "hosted_zone_id" {
value = aws_route53_zone.primary.zone_id
}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/route53$ cat variable.tf
variable "domain_name" {}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/route53$ cat out.tf
output "hosted_zone_id" {
value = aws_route53_zone.primary.zone_id
}
module-certmanager
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/ certmanager$ cat main.tf
resource "aws_acm_certificate" "cert" {
domain_name = var.wild_domain_name
subject_alternative_names = [var.w_domain_name]
validation_method = "DNS"
tags = {
Environment = var.cert_managertag
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_route53_record" "cert" {
for_each = {
for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = var.hosted_zone_id
}
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/ certmanager$ cat variable.tf
variable "wild_domain_name" {
}
variable "w_domain_name" {
}
variable "cert_managertag" {
}
variable "hosted_zone_id" {
}
/* variable "type" {} */
devops@techvoot:~/Documents/terraform-projects/aws/module-aws/modules/certmanager$ cat output.tf
output "aws_acm_certificate_arn"{
value = aws_acm_certificate.cert.arn
}
Crafting Effective Terraform Modules for AWS
Best practices in module development
Explore proven strategies for developing effective Terraform modules, from code structure to documentation. Understand the importance of adherence to best practices.
Reusability and modularity
Dive into the principles of reusability and modularity, showcasing how well-designed modules can be applied across projects and teams.
Conclusion
In this blog journey, we embarked on an exploration of building a robust and scalable AWS infrastructure using Terraform. The orchestrated configuration covered a spectrum of AWS services, each playing a crucial role in ensuring a secure, performant, and highly available environment for your applications.