AWS Terraform Modules: Unleashing the Power of Infrastructure as Code

AWS Terraform Modules: Unleashing the Power of Infrastructure as Code

Hiral Shah

17 Jan 2024

13 MINUTES READ

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.


Hiral Shah
Hiral Shah

DevOps Engineer at Techvoot solutions

Hiral Shah is a proficient DevOps engineer, specializing in automation and cloud technologies. Her skills enhance collaboration and efficiency within development and operations teams, ensuring project success.

Linkedin
Hire Skilled Developer

*Please fill all the required fields

Get our Newsletter

Customized solutions for your projects

*Please fill all the required fields