Pages

Terraform

Terraform is an open-source Infrastructure as Code (IaC) tool created by HashiCorp that lets you define and manage infrastructure using configuration files instead of manual setup.

Terraform uses a declarative language (HCL – HashiCorp Configuration Language).

Terraform builds a dependency graph and executes resources in the correct order automatically.

Install Terraform:
Install yum-config-manager to manage your repositories.
$ sudo yum install -y yum-utils
Use yum-config-manager to add the official HashiCorp RHEL repository.
$ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
Install Terraform from the new repository.
$ sudo yum -y install terraform
Verify the Installation
$ terraform -help

Terraform works in a lifecycle workflow:
Write config → Initialize → Validate → Plan → Apply → Manage state → Destroy (if needed)

terraform init → Initialize Project
  • Downloads required provider plugins (AWS, Azure, etc.)
  • Initializes backend (where state file is stored)
  • Creates hidden .terraform/ directory
  • Downloads modules
  • Prepares working directory
terraform validate → Validate Configuration
  • Checks syntax and internal consistency
  • Does NOT access cloud provider
terraform fmt → Format Code
Automatically formats .tf files to standard style

terraform plan → Preview Changes
  • Reads .tf configuration files
  • Reads current state file
  • Refreshes real infrastructure state
  • Connects to cloud provider (e.g., Amazon Web Services)
  • Shows execution plan (create/update/delete)
terraform apply → Apply Changes
  • Re-runs plan
  • Asks for confirmation (unless -auto-approve)
  • Calls provider APIs
  • Creates/updates/deletes resources
  • Updates terraform.tfstate file
terraform destroy → Remove Infrastructure
  • Reads state file
  • Creates destruction plan
  • Calls provider APIs to delete resources
  • Updates or removes state
terraform import → Import Existing Resource
Brings manually created infrastructure into Terraform state

terraform workspace → Manage Environments
Create/select isolated state environments (dev/qa/prod)
==========================================================================
Terraform Core Concepts:

Provider ---> Plugin that interacts with cloud APIs
Resource ---> Infrastructure object (EC2, VNet, VM, etc.)
Data Source ---> Read-only lookup of existing resources
Module ---> Reusable Terraform configuration
State File ---> Maps configuration to real infrastructure
Backend ---> Where state is stored (local or remote)
Provisioner ---> Executes scripts on resources (last resort)
Variables ---> Input parameters
Outputs ---> Return values after apply
Locals ---> Computed reusable values
Dependency Graph ---> Determines execution order automatically

=========================================================================
Terraform State Management:

terraform.tfstate contains:
  • Resource mappings
  • Metadata
  • Sometimes sensitive values
Remote backends recommended:
AWS ---> S3 + DynamoDB locking
Azure ---> Blob Storage backend
GCP ---> GCS backend

State Locking prevents concurrent modifications.

Sensitive variables should use:
sensitive = true
==========================================================================
Terraform configuration files and structure components:
(Note: File names are conventions; Terraform loads all .tf files automatically.)

main.tf ---> Primary configuration (resources + modules wiring)
variables.tf ---> Input variable definitions
terraform.tfvars ---> Variable values
backend.tf ---> Remote backend configuration
providers.tf ---> Provider configuration & authentication
outputs.tf ---> Output values
data.tf ---> Data sources
locals.tf ---> Local computed values
versions.tf ---> Required Terraform & provider versions
scripts/ ---> User data or bootstrap scripts
modules/ ---> Reusable Terraform modules

=========================================================================
Terraform Variable Types:
string ---> Text value
number ---> Integer or float
bool ---> true / false
list(type) ---> Ordered same-type values
map(type) ---> Key-value same-type values
set(type) ---> Unordered unique values
object({}) ---> Structured attributes
tuple([]) ---> Fixed position mixed types
any ---> Any type

Additional Features:
optional() ---> Optional object attributes
validation {} ---> Custom validation rules
nullable ---> Allow null values
=========================================================================

Complete structured list of Terraform input variables for AWS, Azure, GCP, and VMware:

==================================AWS Inputs ==================================
Provider: Amazon Web Services

aws_region --> string --> AWS region
env --> string --> Environment name (sandbox, qa, prod)
tags --> map(string) --> Common resource tags
tfstate_bucket --> string --> S3 remote state bucket
tfstate_lock_table --> string --> DynamoDB lock table
vpc_cidr --> string --> VPC CIDR
public_subnets --> list(string) --> Public subnet CIDRs
app_subnets --> list(string) --> App subnet CIDRs
db_subnets --> list(string) --> DB subnet CIDRs
azs --> list(string) --> Availability Zones
enable_nat_gateway --> bool --> Enable NAT
nat_gateway_count --> number --> Number of NAT gateways
enable_flow_logs --> bool --> Enable flow logs
kms_key_id --> string --> KMS key for encryption
golden_ami_id --> string --> AMI ID
instance_type --> string --> EC2 type
iam_role_arn --> string --> IAM role
asg_min_size --> number --> ASG min
asg_max_size --> number --> ASG max
asg_desired_count --> number --> ASG desired
rds_engine --> string --> DB engine
rds_instance_class --> string --> DB class
rds_storage_gb --> number --> DB storage
enable_rds_multi_az --> bool --> Multi-AZ RDS
backup_retention_days --> number --> RDS backup retention

=================================== Azure Inputs ===================================
Provider: Microsoft Azure

azure_subscription_id --> string --> Azure subscription ID
azure_tenant_id --> string --> Tenant ID
azure_client_id --> string --> Service principal ID
azure_client_secret --> string --> Service principal secret
azure_location --> string --> Region
azure_resource_group --> string --> Resource group
azure_vnet_cidr --> string --> VNet CIDR
azure_subnets_public --> list(string) --> Public subnets
azure_subnets_app --> list(string) --> App subnets
azure_subnets_db --> list(string) --> DB subnets
azure_vm_size --> string --> VM size
azure_admin_username --> string --> Admin username
azure_admin_password --> string --> Admin password
azure_vm_count --> number --> VM count
azure_tags --> map(string) --> Resource tags
azure_enable_diagnostics --> bool --> Enable diagnostics

================================= GCP Inputs =================================
Provider: Google Cloud Platform
gcp_project_id --> string --> Project ID
gcp_region --> string --> Region
gcp_zone --> string --> Zone
gcp_credentials_file --> string --> Credentials JSON
gcp_network_cidr --> string --> VPC CIDR
gcp_subnets_public --> list(string) --> Public subnets
gcp_subnets_app --> list(string) --> App subnets
gcp_subnets_db --> list(string) --> DB subnets
gcp_instance_type --> string --> VM type
gcp_machine_image --> string --> Image
gcp_disk_size_gb --> number --> Disk size
gcp_disk_type --> string --> Disk type
gcp_vm_count --> number --> VM count
gcp_autoscaling_enabled --> bool --> Autoscaling
gcp_labels --> map(string) --> Resource labels

=============================== VMware Inputs ===============================
Provider: VMware
vsphere_server --> string --> vCenter server
vsphere_user --> string --> Username
vsphere_password --> string --> Password
vsphere_allow_unverified_ssl--> bool --> Allow self-signed SSL
vmware_datacenter --> string --> Datacenter
vmware_cluster --> string --> Cluster
vmware_datastore --> string --> Datastore
vmware_resource_pool --> string --> Resource pool
vmware_network_cidr --> string --> Network CIDR
vmware_template --> string --> VM template
vmware_vm_size --> string --> VM size
vmware_vm_count --> number --> VM count
vmware_dns_servers --> list(string) --> DNS servers
vmware_gateway --> string --> Default gateway
=======================================================================================================================
Enterprise AWS Terraform Landing Zone Structure:
Example:
terraform-aws-landing-zone/
├── modules/
│   ├── network/             # VPC + Subnets (web/app/db) + IGW + NAT HA (4 AZ)
│   ├── nacl/                # Public & private NACLs
│   ├── tgw/                 # Transit Gateway + route propagation + TGW associations/peering
│   ├── flowlogs/            # VPC Flow Logs
│   ├── segmentation/        # Web/App/DB subnet segmentation (optional)
│   ├── security-groups/     # Tiered SG model (Web/App/DB)
│   ├── alb/                 # Application Load Balancer + WAF + ACM
│   ├── nlb/                 # Network Load Balancer
│   ├── compute/             # EC2 + ASG + Launch Template + Golden AMI
│   ├── bastion/             # Bastion / Jump Box
│   ├── cloudwatch-alarms/   # CPU / Memory / Custom CloudWatch alarms
│   ├── pacemaker/           # Overlay/floating IP HA cluster
│   ├── inspection-vpc/      # Firewall / inspection VPC + public/private subnets
│   └── vpc-connection/      # VPC Peering / VPN module
├── envs/
│   ├── project-template/    # Template environment (sandbox)
│   │   ├── main.tf          # Wire all modules together
│   │   ├── variables.tf     # Environment variables
│   │   ├── terraform.tfvars # Example values for sendbox
│   │   ├── backend.tf       # Remote state S3 + DynamoDB
│   │   └── scripts/         # Scripts like pacemaker_setup.sh
└── global/
    ├── accounts.tf          # Hub-Spoke multi-account mapping
    ├── backend.tf           # Shared backend configuration (optional)
    └── providers.tf         # Shared provider configuration (optional)
=======================================================================================================================
Example: modules/network/main.tf

# VPC
resource "aws_vpc" "this" {
  cidr_block           = var.vpc_cidr  # change as needed
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name        = "${var.env}-vpc"  # change env in variables.tf or tfvars
    Environment = var.env           # change as needed
  }
}

# Internet Gateway
resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.this.id
  tags = {
    Name        = "${var.env}-igw"  # change env in variables.tf or tfvars
    Environment = var.env           # change as needed
  }
}

# Public Subnets
resource "aws_subnet" "public" {
  for_each = { for idx, cidr in var.public_subnets : idx => cidr }
  vpc_id                  = aws_vpc.this.id
  cidr_block              = each.value  # change in tfvars
  availability_zone       = var.azs[each.key]  # change in tfvars
  map_public_ip_on_launch = true
  tags = {
    Name        = "${var.env}-public-${each.key}"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
}

# App (Private) Subnets
resource "aws_subnet" "app" {
  for_each = { for idx, cidr in var.app_subnets : idx => cidr }
  vpc_id                  = aws_vpc.this.id
  cidr_block              = each.value  # change in tfvars
  availability_zone       = var.azs[each.key]  # change in tfvars
  map_public_ip_on_launch = false
  tags = {
    Name        = "${var.env}-app-${each.key}"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
}

# DB (Private) Subnets
resource "aws_subnet" "db" {
  for_each = { for idx, cidr in var.db_subnets : idx => cidr }
  vpc_id                  = aws_vpc.this.id
  cidr_block              = each.value  # change in tfvars
  availability_zone       = var.azs[each.key]  # change in tfvars
  map_public_ip_on_launch = false
  tags = {
    Name        = "${var.env}-db-${each.key}"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
}

# NAT Gateways (HA)
resource "aws_eip" "nat" {
  for_each = aws_subnet.public
  vpc = true
  tags = {
    Name        = "${var.env}-nat-${each.key}"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
}

resource "aws_nat_gateway" "this" {
  for_each = aws_subnet.public
  allocation_id = aws_eip.nat[each.key].id
  subnet_id     = each.value.id
  tags = {
    Name        = "${var.env}-nat-${each.key}"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
  depends_on = [aws_internet_gateway.this]
}

# Public Route Table
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.this.id
  tags = {
    Name        = "${var.env}-public-rt"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
}

resource "aws_route" "public_internet" {
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"  # default internet route
  gateway_id             = aws_internet_gateway.this.id
}

resource "aws_route_table_association" "public" {
  for_each       = aws_subnet.public
  subnet_id      = each.value.id
  route_table_id = aws_route_table.public.id
}

# Private Route Tables for App Subnets
resource "aws_route_table" "private_app" {
  for_each = aws_subnet.app
  vpc_id = aws_vpc.this.id
  tags = {
    Name        = "${var.env}-private-rt-app-${each.key}"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
}

resource "aws_route" "private_app_nat" {
  for_each = aws_route_table.private_app
  route_table_id         = each.value.id
  destination_cidr_block = "0.0.0.0/0"  # route through NAT
  nat_gateway_id         = aws_nat_gateway.this[each.key].id
}

resource "aws_route_table_association" "private_app" {
  for_each       = aws_subnet.app
  subnet_id      = each.value.id
  route_table_id = aws_route_table.private_app[each.key].id
}

# Private Route Tables for DB Subnets
resource "aws_route_table" "private_db" {
  for_each = aws_subnet.db
  vpc_id = aws_vpc.this.id
  tags = {
    Name        = "${var.env}-private-rt-db-${each.key}"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
}

resource "aws_route" "private_db_nat" {
  for_each = aws_route_table.private_db
  route_table_id         = each.value.id
  destination_cidr_block = "0.0.0.0/0"  # route through NAT
  nat_gateway_id         = aws_nat_gateway.this[each.key].id
}

resource "aws_route_table_association" "private_db" {
  for_each       = aws_subnet.db
  subnet_id      = each.value.id
  route_table_id = aws_route_table.private_db[each.key].id
}

Example: modules/network/variables.tf

variable "vpc_cidr" {
  type    = string
  default = "10.0.0.0/16"  # change as needed
}

variable "public_subnets" {
  type    = list(string)
  default = ["10.0.1.0/24","10.0.2.0/24","10.0.3.0/24"]  # change as needed
}

variable "app_subnets" {
  type    = list(string)
  default = ["10.0.11.0/24","10.0.12.0/24","10.0.13.0/24"]  # change as needed
}

variable "db_subnets" {
  type    = list(string)
  default = ["10.0.21.0/24","10.0.22.0/24","10.0.23.0/24"]  # change as needed
}

variable "azs" {
  type    = list(string)
  default = ["us-east-1a","us-east-1b","us-east-1c"]  # change as needed
}

variable "env" {
  type    = string
  default = "sendbox"  # change to your environment name
}

Example: modules/network/outputs.tf

output "vpc_id" {
  value = aws_vpc.this.id  # VPC ID
}

output "public_subnets" {
  value = [for s in aws_subnet.public : s.id]  # list of public subnet IDs
}

output "app_subnets" {
  value = [for s in aws_subnet.app : s.id]  # list of app (private) subnet IDs
}

output "db_subnets" {
  value = [for s in aws_subnet.db : s.id]  # list of db (private) subnet IDs
}

output "nat_gateway_ids" {
  value = [for ngw in aws_nat_gateway.this : ngw.id]  # NAT Gateway IDs
}

output "private_app_route_table_ids" {
  value = [for rt in aws_route_table.private_app : rt.id]  # private route tables for app subnets
}

output "private_db_route_table_ids" {
  value = [for rt in aws_route_table.private_db : rt.id]  # private route tables for db subnets
}

What to change:
  • env → in variables.tf or via terraform.tfvars
  • vpc_cidr → main VPC CIDR
  • public_subnets, app_subnets, db_subnets → subnet CIDRs
  • azs → availability zones for your subnets
========================================================================================================================
Example: modules/nacl/main.tf

# Public NACL
resource "aws_network_acl" "public" {
  vpc_id = var.vpc_id  # change as needed (pass from network module output)
  tags = {
    Name        = "${var.env}-public-nacl"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
}

# Public NACL Rules
resource "aws_network_acl_rule" "public_inbound" {
  network_acl_id = aws_network_acl.public.id
  rule_number    = 100
  egress         = false
  protocol       = "-1"  # all protocols
  rule_action    = "allow"
  cidr_block     = "0.0.0.0/0"  # change if you want to restrict access
}

resource "aws_network_acl_rule" "public_outbound" {
  network_acl_id = aws_network_acl.public.id
  rule_number    = 100
  egress         = true
  protocol       = "-1"  # all protocols
  rule_action    = "allow"
  cidr_block     = "0.0.0.0/0"  # change if you want to restrict egress
}

# Associate Public NACL with public subnets
resource "aws_network_acl_association" "public" {
  for_each       = toset(var.public_subnets)  # change via tfvars
  subnet_id      = each.value
  network_acl_id = aws_network_acl.public.id
}

# Private NACL (App + DB)
resource "aws_network_acl" "private" {
  vpc_id = var.vpc_id  # change as needed (pass from network module output)
  tags = {
    Name        = "${var.env}-private-nacl"  # change env in variables.tf or tfvars
    Environment = var.env  # change as needed
  }
}

# Private NACL Rules (allow internal traffic)
resource "aws_network_acl_rule" "private_inbound" {
  network_acl_id = aws_network_acl.private.id
  rule_number    = 100
  egress         = false
  protocol       = "-1"
  rule_action    = "allow"
  cidr_block     = var.internal_cidr  # change in variables.tf or tfvars
}

resource "aws_network_acl_rule" "private_outbound" {
  network_acl_id = aws_network_acl.private.id
  rule_number    = 100
  egress         = true
  protocol       = "-1"
  rule_action    = "allow"
  cidr_block     = var.internal_cidr  # change in variables.tf or tfvars
}

# Optional: allow NAT access to the internet
resource "aws_network_acl_rule" "private_outbound_nat" {
  network_acl_id = aws_network_acl.private.id
  rule_number    = 110
  egress         = true
  protocol       = "-1"
  rule_action    = "allow"
  cidr_block     = "0.0.0.0/0"  # change if you want restricted internet access
}

# Associate Private NACL with app subnets
resource "aws_network_acl_association" "private_app" {
  for_each       = toset(var.app_subnets)  # change via tfvars
  subnet_id      = each.value
  network_acl_id = aws_network_acl.private.id
}

# Associate Private NACL with DB subnets
resource "aws_network_acl_association" "private_db" {
  for_each       = toset(var.db_subnets)  # change via tfvars
  subnet_id      = each.value
  network_acl_id = aws_network_acl.private.id
}

Example: modules/nacl/variables.tf

variable "vpc_id" {
  type = string  # pass VPC ID from network module output
}

variable "public_subnets" {
  type = list(string)  # pass public subnet IDs
}

variable "app_subnets" {
  type = list(string)  # pass app subnet IDs
}

variable "db_subnets" {
  type = list(string)  # pass db subnet IDs
}

variable "internal_cidr" {
  type    = string
  default = "10.0.0.0/16"  # change to match your VPC
}

variable "env" {
  type    = string
  default = "sendbox"  # change to your environment name
}

Example: modules/nacl/outputs.tf

output "public_nacl_id" {
  value = aws_network_acl.public.id  # Public NACL ID
}

output "private_nacl_id" {
  value = aws_network_acl.private.id  # Private NACL ID
}

Summary of Updates
  • Added # change as needed comments to all resources, rules, and variables.
  • Added Environment = var.env tags for consistency.
  • Clarified which variables come from the network module outputs.
  • Ensured all associations (public/app/db) are explicit and ready to customize.
========================================================================================================================
Example: modules/tgw/main.tf

# Create Transit Gateway
resource "aws_ec2_transit_gateway" "tgw" {
  description = "${var.env}-${var.resource_name}-tgw"    # change environment name and resource
  amazon_side_asn = var.amazon_side_asn
  auto_accept_shared_attachments = var.auto_accept_shared
  default_route_table_association = var.default_association
  default_route_table_propagation = var.default_propagation
  dns_support = var.dns_support
  vpn_ecmp_support = var.vpn_ecmp_support
  tags = {
    Name = "${var.env}-${var.resource_name}-tgw"        # change environment name and resource
    Env  = var.env                                      # change environment name
  }
}

# Attach VPCs to TGW
resource "aws_ec2_transit_gateway_vpc_attachment" "tgw_vpc" {
  for_each = var.vpc_attachments

  transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  vpc_id             = each.value.vpc_id                 # change VPC ID
  subnet_ids         = each.value.subnet_ids             # change Subnet IDs
  tags = {
    Name = "${var.env}-${var.resource_name}-vpc-${each.key}"  # change environment name and resource
    Env  = var.env
  }
}

# Associate VPC attachments with TGW Route Tables
resource "aws_ec2_transit_gateway_route_table_association" "tgw_assoc" {
  for_each = var.vpc_attachments

  transit_gateway_attachment_id = aws_ec2_transit_gateway_vpc_attachment.tgw_vpc[each.key].id
  transit_gateway_route_table_id = var.route_table_id   # change TGW route table ID
}

# Transit Gateway Peering (optional)
resource "aws_ec2_transit_gateway_peering_attachment" "tgw_peer" {
  count = var.enable_peering ? 1 : 0

  peer_transit_gateway_id = var.peer_tgw_id           # change peer TGW ID
  transit_gateway_id      = aws_ec2_transit_gateway.tgw.id
  peer_account_id         = var.peer_account_id       # change AWS account ID
  peer_region             = var.peer_region          # change peer region
  tags = {
    Name = "${var.env}-${var.resource_name}-tgw-peer"  # change environment name and resource
    Env  = var.env
  }
}

# Optionally associate peering with TGW route table
resource "aws_ec2_transit_gateway_peering_attachment_accepter" "peer_accept" {
  count = var.enable_peering ? 1 : 0
  transit_gateway_attachment_id = aws_ec2_transit_gateway_peering_attachment.tgw_peer[0].id
  auto_accept = true
}

Example: modules/tgw/variables.tf

# Environment name
variable "env" {
  type    = string
  default = "sendbox"       # change environment name
}

# Resource identifier
variable "resource_name" {
  type = string             # change resource name
}

# TGW options
variable "amazon_side_asn" {
  type    = number
  default = 64512
}
variable "auto_accept_shared" {
  type    = string
  default = "disable"
}
variable "default_association" {
  type    = string
  default = "enable"
}
variable "default_propagation" {
  type    = string
  default = "enable"
}
variable "dns_support" {
  type    = string
  default = "enable"
}
variable "vpn_ecmp_support" {
  type    = string
  default = "enable"
}

# VPC Attachments
variable "vpc_attachments" {
  type = map(object({
    vpc_id     = string
    subnet_ids = list(string)
  }))
  default = {}   # example: { "app" = { vpc_id = "vpc-123", subnet_ids = ["subnet-1","subnet-2"] } }
}

# TGW Route Table ID for associations
variable "route_table_id" {
  type = string   # change TGW route table ID
}

# TGW Peering options
variable "enable_peering" {
  type    = bool
  default = false
}
variable "peer_tgw_id" {
  type    = string
  default = ""     # change peer TGW ID
}
variable "peer_account_id" {
  type    = string
  default = ""     # change peer AWS account ID
}
variable "peer_region" {
  type    = string
  default = ""     # change peer region
}

Example: modules/tgw/outputs.tf

# TGW ID
output "tgw_id" {
  value = aws_ec2_transit_gateway.tgw.id
}

# VPC attachment IDs
output "tgw_vpc_attachment_ids" {
  value = { for k, v in aws_ec2_transit_gateway_vpc_attachment.tgw_vpc : k => v.id }
}

# TGW Peering ID
output "tgw_peering_id" {
  value = var.enable_peering ? aws_ec2_transit_gateway_peering_attachment.tgw_peer[0].id : ""
}
Example Usage
module "tgw" {
  source        = "./modules/tgw"
  env           = "prod"                             # change environment name
  resource_name = "core-network"                     # change resource name

  amazon_side_asn = 64512
  auto_accept_shared = "disable"
  default_association = "enable"
  default_propagation = "enable"
  dns_support = "enable"
  vpn_ecmp_support = "enable"

  vpc_attachments = {
    "app" = { vpc_id = aws_vpc.app.id, subnet_ids = [aws_subnet.app1.id, aws_subnet.app2.id] }
    "db"  = { vpc_id = aws_vpc.db.id, subnet_ids  = [aws_subnet.db1.id, aws_subnet.db2.id] }
  }

  route_table_id = aws_ec2_transit_gateway_route_table.main.id

  enable_peering   = true
  peer_tgw_id      = "tgw-0123456789abcdef0"
  peer_account_id  = "123456789012"
  peer_region      = "us-east-1"
}

What to change:
  • env → environment name (tags & resource names)
  • resource_name → TGW resource identifier
  • VPC Attachments: vpc_id, subnet_ids, route_table_id
  • TGW Peering: peer_tgw_id, peer_account_id, peer_region
  • Optional TGW settings: amazon_side_asn, dns_support, etc.
========================================================================================================================
Example: modules/flowlogs/main.tf

# CloudWatch Log Group for VPC Flow Logs
resource "aws_cloudwatch_log_group" "vpc_flowlogs" {
  name              = var.log_group            # change log group name if needed
  retention_in_days = var.retention_in_days    # change retention period if needed
  tags = {
    Name        = "${var.env}-vpc-flowlogs"  # change env in variables.tf or tfvars
    Environment = var.env                    # change as needed
  }
}

# VPC Flow Log
resource "aws_flow_log" "vpc" {
  vpc_id               = var.vpc_id           # change VPC ID (pass from network module)
  traffic_type         = var.traffic_type     # ACCEPT, REJECT, or ALL
  log_destination_type = "cloud-watch-logs"
  log_group_name       = aws_cloudwatch_log_group.vpc_flowlogs.name
  iam_role_arn         = var.iam_role_arn     # IAM role ARN with CloudWatch permissions
  tags = {
    Name        = "${var.env}-vpc-flowlog"  # change env in variables.tf or tfvars
    Environment = var.env                  # change as needed
  }
}

Example: modules/flowlogs/variables.tf

# VPC ID to attach Flow Logs
variable "vpc_id" {
  type = string  # pass the VPC ID (e.g., module.network.vpc_id)
}

# CloudWatch Log Group name
variable "log_group" {
  type    = string
  default = "/vpc/flowlogs/sendbox"  # change as needed (e.g., "/vpc/flowlogs/prod")
}

# Log retention in days
variable "retention_in_days" {
  type    = number
  default = 90  # change as needed
}

# Traffic type to capture
variable "traffic_type" {
  type        = string
  default     = "ALL"  # change to ACCEPT or REJECT if desired
  description = "Type of traffic to capture: ACCEPT, REJECT, or ALL"
}

# IAM Role ARN for Flow Logs
variable "iam_role_arn" {
  type        = string
  description = "IAM role ARN for Flow Logs to publish to CloudWatch Logs"  # change as needed
}

# Environment name
variable "env" {
  type    = string
  default = "sendbox"  # change to your environment name (prod, dev, etc.)
}

Example: modules/flowlogs/outputs.tf

# Flow Log ID
output "flow_log_id" {
  value = aws_flow_log.vpc.id  # VPC Flow Log ID
}

# CloudWatch Log Group name
output "log_group_name" {
  value = aws_cloudwatch_log_group.vpc_flowlogs.name  # CloudWatch Log Group name
}

Summary of Updates
  • Added # change as needed comments for all key parameters (VPC ID, log group, retention, traffic type, IAM role, env).
  • Added Environment = var.env tags for consistency across modules.
  • Clarified how variables map to outputs from the network module.
  • Fully ready customization (prod/dev) with minimal edits.
========================================================================================================================
Example: modules/security-groups/main.tf

# Web Security Group
resource "aws_security_group" "web" {
  name        = "${var.env}-web-sg"      # change env as needed
  description = "Web tier security group"
  vpc_id      = var.vpc_id               # change VPC ID
  tags = {
    Name        = "${var.env}-web-sg"    # change env as needed
    Environment = var.env                 # change as needed
  }
}

# Inbound rules for Web SG
resource "aws_security_group_rule" "web_inbound_http" {
  type              = "ingress"
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = var.public_cidr_blocks  # change source CIDRs as needed
  security_group_id = aws_security_group.web.id
}

resource "aws_security_group_rule" "web_inbound_https" {
  type              = "ingress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  cidr_blocks       = var.public_cidr_blocks  # change source CIDRs as needed
  security_group_id = aws_security_group.web.id
}

# Outbound: allow all
resource "aws_security_group_rule" "web_outbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]          # change if needed
  security_group_id = aws_security_group.web.id
}

# App Security Group
resource "aws_security_group" "app" {
  name        = "${var.env}-app-sg"      # change env as needed
  description = "App tier security group"
  vpc_id      = var.vpc_id               # change VPC ID
  tags = {
    Name        = "${var.env}-app-sg"    # change env as needed
    Environment = var.env                 # change as needed
  }
}

# Inbound from Web SG
resource "aws_security_group_rule" "app_inbound_from_web" {
  type                     = "ingress"
  from_port                = 0
  to_port                  = 65535
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.web.id
  security_group_id        = aws_security_group.app.id
}

# Outbound to DB SG
resource "aws_security_group_rule" "app_outbound_to_db" {
  type                          = "egress"
  from_port                     = 0
  to_port                       = 65535
  protocol                      = "tcp"
  destination_security_group_id = aws_security_group.db.id
  security_group_id             = aws_security_group.app.id
}

# DB Security Group
resource "aws_security_group" "db" {
  name        = "${var.env}-db-sg"       # change env as needed
  description = "DB tier security group"
  vpc_id      = var.vpc_id               # change VPC ID
  tags = {
    Name        = "${var.env}-db-sg"     # change env as needed
    Environment = var.env                 # change as needed
  }
}

# Inbound from App SG
resource "aws_security_group_rule" "db_inbound_from_app" {
  type                     = "ingress"
  from_port                = 0
  to_port                  = 65535
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.app.id
  security_group_id        = aws_security_group.db.id
}

# Outbound: allow all
resource "aws_security_group_rule" "db_outbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]        # change if needed
  security_group_id = aws_security_group.db.id
}

Example: modules/security-groups/variables.tf

# VPC ID
variable "vpc_id" {
  type = string  # pass from network module output (e.g., module.network.vpc_id)
}

# Public CIDR blocks allowed to access Web SG
variable "public_cidr_blocks" {
  type    = list(string)
  default = ["0.0.0.0/0"]  # change to restrict access
}

# Environment name
variable "env" {
  type    = string
  default = "sendbox"  # change to your environment name (prod, dev, etc.)
}

Example: modules/security-groups/outputs.tf

# Web SG ID
output "web_sg_id" {
  value = aws_security_group.web.id  # Web Security Group ID
}

# App SG ID
output "app_sg_id" {
  value = aws_security_group.app.id  # App Security Group ID
}

# DB SG ID
output "db_sg_id" {
  value = aws_security_group.db.id   # DB Security Group ID
}

Summary of Updates
  • Added # change as needed comments for all names, CIDRs, and VPC IDs.
  • Added consistent Environment = var.env tags.
  • Clarified which variables come from the network module outputs.
  • Rules (inbound/outbound) fully annotated for easy customization (ports, source/destination, CIDRs).
========================================================================================================================
Example: modules/alb/main.tf

# Application Load Balancer
resource "aws_lb" "this" {
  name                       = "${var.env}-alb"        # change env as needed
  internal                   = false                    # set true if internal ALB
  load_balancer_type         = "application"
  security_groups            = [var.web_sg_id]         # pass Web SG ID
  subnets                    = var.public_subnets      # pass public subnet IDs
  enable_deletion_protection = false                    # change if needed
  tags = {
    Name        = "${var.env}-alb"                    # change env as needed
    Environment = var.env                              # change as needed
  }
}

# ALB Listener (HTTPS)
resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.this.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  certificate_arn   = var.certificate_arn            # change to ACM certificate ARN
  default_action {
    type = "fixed-response"
    fixed_response {
      content_type = "text/plain"
      message_body = "OK"                             # change default response if needed
      status_code  = 200
    }
  }
}

# Optional WAFv2 Web ACL association
resource "aws_wafv2_web_acl_association" "this" {
  count        = var.waf_acl_id != "" ? 1 : 0
  resource_arn = aws_lb.this.arn
  web_acl_arn  = var.waf_acl_id                      # optional WAF ACL ARN
}

Example: modules/alb/variables.tf

variable "vpc_id" {
  type = string           # pass VPC ID (not used directly, keep for reference)
}

variable "public_subnets" {
  type = list(string)     # pass public subnet IDs from network module
}

variable "web_sg_id" {
  type = string           # pass Web SG ID from security-groups module
}

variable "certificate_arn" {
  type        = string
  description = "ACM certificate ARN for HTTPS"  # change to your certificate ARN
}

variable "waf_acl_id" {
  type        = string
  default     = ""                               # optional WAFv2 ACL ARN
  description = "Optional WAFv2 Web ACL ARN"
}

variable "env" {
  type    = string
  default = "sendbox"                            # change to your environment name
}

Example: modules/alb/outputs.tf

# ALB ARN
output "alb_arn" {
  value = aws_lb.this.arn  # Application Load Balancer ARN
}

# ALB DNS Name
output "alb_dns_name" {
  value = aws_lb.this.dns_name  # ALB DNS Name
}

# ALB Listener ARN
output "alb_listener_arn" {
  value = aws_lb_listener.https.arn  # ALB Listener ARN
}

Summary of Updates
  • Added # change as needed for all key parameters (env, subnets, security_groups, certificate_arn, internal).
  • Added Environment = var.env tag for consistency with other modules.
  • Optional WAF association annotated for clarity (waf_acl_id).
  • Fully customizable for prod/dev with public subnet and Web SG inputs from other modules.
========================================================================================================================
Example: modules/nlb/main.tf

# Network Load Balancer
resource "aws_lb" "this" {
  name                       = "${var.env}-nlb"      # change env as needed
  internal                   = var.internal          # set true for internal NLB
  load_balancer_type         = "network"
  subnets                    = var.subnets          # pass subnet IDs
  enable_deletion_protection = false                 # change if needed
  tags = {
    Name        = "${var.env}-nlb"                 # change env as needed
    Environment = var.env                           # change as needed
  }
}

# TCP Listener (default port 80, can be overridden)
resource "aws_lb_listener" "tcp" {
  load_balancer_arn = aws_lb.this.arn
  port              = var.listener_port           # change listener port if needed
  protocol          = "TCP"
  default_action {
    type = "fixed-response"
    fixed_response {
      content_type = "text/plain"
      message_body = "OK"                          # change response if needed
      status_code  = 200
    }
  }
}

# Optional Target Group
resource "aws_lb_target_group" "this" {
  name        = "${var.env}-tg"                  # change env as needed
  port        = var.target_port                  # change target port if needed
  protocol    = "TCP"
  vpc_id      = var.vpc_id                       # pass VPC ID
  target_type = "instance"
  health_check {
    protocol            = "TCP"
    interval            = 30
    timeout             = 10
    healthy_threshold   = 3
    unhealthy_threshold = 3
  }
  tags = {
    Name        = "${var.env}-tg"               # change env as needed
    Environment = var.env                        # change as needed
  }
}

# Listener forwarding to Target Group
resource "aws_lb_listener_rule" "forward" {
  listener_arn = aws_lb_listener.tcp.arn
  priority     = 100
  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.this.arn
  }
  condition {
    host_header {
      values = ["*"]                             # change if needed for host-based routing
    }
  }
}

Example: modules/nlb/variables.tf

variable "vpc_id" {
  type = string           # pass VPC ID from network module
}

variable "subnets" {
  type = list(string)     # pass subnet IDs (public or private as needed)
}

variable "listener_port" {
  type    = number
  default = 80             # change listener port if needed
}

variable "target_port" {
  type    = number
  default = 80             # change target port if needed
}

variable "internal" {
  type    = bool
  default = false          # set true if internal NLB
}

variable "env" {
  type    = string
  default = "sendbox"      # change environment name (prod, dev, etc.)
}

Example: modules/nlb/outputs.tf

# NLB ARN
output "nlb_arn" {
  value = aws_lb.this.arn      # Network Load Balancer ARN
}

# NLB DNS Name
output "nlb_dns_name" {
  value = aws_lb.this.dns_name # NLB DNS Name
}

# Target Group ARN
output "target_group_arn" {
  value = aws_lb_target_group.this.arn # Target Group ARN
}

Summary of Updates
  • Added # change as needed for all critical parameters (env, subnets, internal, listener_port, target_port, vpc_id).
  • Added consistent Environment = var.env tags.
  • Clarified optional changes in listener rules (host headers, TCP port, response).
  • Fully ready for prod or dev deployment with NLB, listener, and target group connected to your network module.
========================================================================================================================
Example: modules/compute/main.tf

# Launch Template
resource "aws_launch_template" "this" {
  name_prefix   = "${var.env}-lt-"                  # change env as needed
  image_id      = var.ami_id                        # change AMI ID as needed
  instance_type = var.instance_type                 # change instance type as needed
  vpc_security_group_ids = [
    var.web_sg_id,                                  # Web SG ID
    var.app_sg_id,                                  # App SG ID
    var.db_sg_id                                    # DB SG ID
  ]
  key_name = var.key_name                            # change key pair if needed
  tag_specifications {
    resource_type = "instance"
    tags = {
      Name = "${var.env}-instance"                  # change env or instance name if needed
      Env  = var.env                                # environment tag
    }
  }
}

# Auto Scaling Group
resource "aws_autoscaling_group" "this" {
  name_prefix          = "${var.env}-asg-"         # change env as needed
  launch_template {
    id      = aws_launch_template.this.id
    version = "$Latest"
  }
  min_size                = var.asg_min_size       # change min size as needed
  max_size                = var.asg_max_size       # change max size as needed
  desired_capacity        = var.asg_desired_count  # change desired capacity as needed
  vpc_zone_identifier     = var.app_subnets        # subnets for ASG
  health_check_type       = "EC2"
  health_check_grace_period = 300
  force_delete            = true
  tag {
    key                 = "Name"
    value               = "${var.env}-asg-instance"  # change env or name if needed
    propagate_at_launch = true
  }
}

Example: modules/compute/variables.tf

variable "vpc_id" {
  type = string              # pass VPC ID from network module
}

variable "app_subnets" {
  type = list(string)        # pass private app subnet IDs
}

variable "db_subnets" {
  type = list(string)        # pass private DB subnet IDs (optional if used)
}

variable "web_sg_id" {
  type = string              # Web SG ID from security-groups module
}

variable "app_sg_id" {
  type = string              # App SG ID from security-groups module
}

variable "db_sg_id" {
  type = string              # DB SG ID from security-groups module
}

variable "ami_id" {
  type = string              # AMI ID to launch instances
}

variable "instance_type" {
  type    = string
  default = "t3.medium"      # change instance type as needed
}

variable "asg_min_size" {
  type    = number
  default = 1                 # change min size as needed
}

variable "asg_max_size" {
  type    = number
  default = 3                 # change max size as needed
}

variable "asg_desired_count" {
  type    = number
  default = 2                 # change desired capacity as needed
}

variable "key_name" {
  type    = string
  default = ""                # set key pair if SSH access is needed
}

variable "env" {
  type    = string
  default = "sendbox"         # change environment name (prod, dev, etc.)
}

Example: modules/compute/outputs.tf

# Launch Template ID
output "launch_template_id" {
  value = aws_launch_template.this.id  # Launch Template ID
}

# Auto Scaling Group Name
output "asg_name" {
  value = aws_autoscaling_group.this.name  # ASG Name
}

Summary of Updates
  • Added # change as needed for all key parameters (env, ami_id, instance_type, ASG sizes, key_name).
  • Added Env = var.env tag for instances.
  • Clearly referenced Security Groups and subnets from other modules.
  • Fully ready for prod/dev deployment with ASG and Launch Template using your network and security group modules.
========================================================================================================================
Example: modules/pacemaker/main.tf

# Security Group for Pacemaker nodes
resource "aws_security_group" "pacemaker" {
  name        = "${var.env}-pacemaker-sg"        # change env as needed
  description = "Security group for Pacemaker HA overlay nodes"
  vpc_id      = var.vpc_id                       # change VPC ID as needed

  # Corosync & Heartbeat traffic
  ingress {
    description = "Corosync & Heartbeat"
    from_port   = 5405
    to_port     = 5405
    protocol    = "tcp"
    cidr_blocks = var.internal_cidr_blocks       # change internal network CIDR as needed
  }

  # SSH access
  ingress {
    description = "SSH access"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = var.ssh_cidr_blocks            # restrict to your IPs as needed
  }

  # Outbound: allow all
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]                  # change if needed
  }

  tags = {
    Name = "${var.env}-pacemaker-sg"            # change env as needed
    Env  = var.env                               # environment tag
  }
}

# Pacemaker EC2 nodes
resource "aws_instance" "pacemaker_nodes" {
  count = length(var.private_subnets)            # one node per private subnet
  ami           = var.ami_id                     # change AMI ID as needed
  instance_type = var.instance_type              # change instance type as needed
  subnet_id     = var.private_subnets[count.index]
  vpc_security_group_ids = [aws_security_group.pacemaker.id]
  key_name      = var.key_name                   # change key pair if needed
  tags = {
    Name = "${var.env}-pacemaker-${count.index}" # change env or node naming if needed
    Env  = var.env
  }
  user_data = file(var.user_data_script)         # change path to Pacemaker setup script
}

# Elastic IP for Floating VIP
resource "aws_eip" "vip" {
  vpc = true
  depends_on = [aws_instance.pacemaker_nodes]    # ensure instances exist before allocation
}

# Note: Floating VIP configuration must be handled in Pacemaker/Corosync user_data script

Example: modules/pacemaker/variables.tf

variable "vpc_id" {
  type = string               # pass VPC ID from network module
}

variable "private_subnets" {
  type = list(string)         # private subnets for Pacemaker nodes
}

variable "ami_id" {
  type = string               # AMI ID for Pacemaker EC2 instances
}

variable "instance_type" {
  type    = string
  default = "t3.medium"       # change instance type as needed
}

variable "key_name" {
  type    = string
  default = ""                 # SSH key pair
}

variable "internal_cidr_blocks" {
  type    = list(string)
  default = ["10.0.0.0/16"]   # internal network CIDR for Corosync/Heartbeat
}

variable "ssh_cidr_blocks" {
  type    = list(string)
  default = ["0.0.0.0/0"]     # change to restricted IP range for SSH
}

variable "user_data_script" {
  type        = string
  description = "Path to script installing/configuring Pacemaker and Floating VIP"  # change as needed
}

variable "env" {
  type    = string
  default = "sendbox"         # change environment name (prod, dev, etc.)
}

Example: modules/pacemaker/outputs.tf

# Pacemaker EC2 Node IDs
output "pacemaker_node_ids" {
  value = [for node in aws_instance.pacemaker_nodes : node.id]  # list of node IDs
}

# Floating VIP Elastic IP
output "vip_eip" {
  value = aws_eip.vip.public_ip  # Elastic IP for floating VIP
}

Summary of Updates
  • Added # change as needed for all key parameters (env, ami_id, instance_type, key_name, internal_cidr_blocks, ssh_cidr_blocks, user_data_script).
  • Added Env = var.env tag to all EC2 nodes and security group.
  • Clarified which CIDRs are internal vs. SSH.
  • Configured Pacemaker nodes per private subnet with a floating VIP EIP ready for HA setup.
========================================================================================================================
Example: modules/inspection-vpc/main.tf

# Inspection VPC
resource "aws_vpc" "inspection" {
  cidr_block           = var.vpc_cidr              # change VPC CIDR as needed
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = "${var.env}-inspection-vpc"           # change env as needed
    Env  = var.env                               # environment tag
  }
}

# Public subnets for firewall/load balancer
resource "aws_subnet" "public" {
  for_each = { for idx, cidr in var.public_subnets : idx => cidr }
  vpc_id            = aws_vpc.inspection.id
  cidr_block        = each.value
  availability_zone = var.azs[each.key]          # change AZs as needed
  map_public_ip_on_launch = true
  tags = {
    Name = "${var.env}-inspection-public-${each.key}"  # change env as needed
    Env  = var.env
  }
}

# Private subnets for internal inspection traffic
resource "aws_subnet" "private" {
  for_each = { for idx, cidr in var.private_subnets : idx => cidr }
  vpc_id            = aws_vpc.inspection.id
  cidr_block        = each.value
  availability_zone = var.azs[each.key]
  map_public_ip_on_launch = false
  tags = {
    Name = "${var.env}-inspection-private-${each.key}" # change env as needed
    Env  = var.env
  }
}

# Internet Gateway for inspection VPC (optional)
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.inspection.id
  tags = {
    Name = "${var.env}-inspection-igw"          # change env as needed
    Env  = var.env
  }
}

# Public Route Table
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.inspection.id
  tags = {
    Name = "${var.env}-inspection-public-rt"   # change env as needed
    Env  = var.env
  }
}

resource "aws_route" "public_internet" {
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.igw.id
}

resource "aws_route_table_association" "public_assoc" {
  for_each       = aws_subnet.public
  subnet_id      = each.value.id
  route_table_id = aws_route_table.public.id
}

# Private Route Table
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.inspection.id
  tags = {
    Name = "${var.env}-inspection-private-rt"  # change env as needed
    Env  = var.env
  }
}

resource "aws_route_table_association" "private_assoc" {
  for_each       = aws_subnet.private
  subnet_id      = each.value.id
  route_table_id = aws_route_table.private.id
}

Example: modules/inspection-vpc/variables.tf

variable "vpc_cidr" {
  type = string                  # CIDR block for Inspection VPC
}

variable "public_subnets" {
  type = list(string)            # list of public subnet CIDRs for firewall/load balancer
}

variable "private_subnets" {
  type = list(string)            # list of private subnet CIDRs for internal inspection traffic
}

variable "azs" {
  type = list(string)            # availability zones for the subnets
}

variable "env" {
  type    = string
  default = "sendbox"            # change environment name (prod, dev, etc.)
}

Example: modules/inspection-vpc/outputs.tf

# Inspection VPC ID
output "inspection_vpc_id" {
  value = aws_vpc.inspection.id
}

# Public Subnet IDs
output "public_subnet_ids" {
  value = [for s in aws_subnet.public : s.id]
}

# Private Subnet IDs
output "private_subnet_ids" {
  value = [for s in aws_subnet.private : s.id]
}

What to change
  • env → environment name for tags (prod, dev, etc.).
  • vpc_cidr → CIDR block for the Inspection VPC.
  • public_subnets → list of public subnet CIDRs for firewall/load balancer.
  • private_subnets → list of private subnet CIDRs for internal inspection traffic.
  • azs → availability zones corresponding to each subnet.
========================================================================================================================
Example: modules/rds/main.tf

# RDS Subnet Group
resource "aws_db_subnet_group" "this" {
  name       = "${var.env}-rds-subnet-group"       # change env as needed
  subnet_ids = var.db_subnets                      # private DB subnet IDs
  tags = {
    Name = "${var.env}-rds-subnet-group"          # change env as needed
    Env  = var.env
  }
}

# RDS Security Group (optional, default allows all in VPC)
resource "aws_security_group" "rds_sg" {
  name        = "${var.env}-rds-sg"               # change env as needed
  description = "Security group for RDS instances"
  vpc_id      = var.vpc_id                        # VPC ID
  # inbound from app servers
  ingress {
    from_port                = var.db_port
    to_port                  = var.db_port
    protocol                 = "tcp"
    source_security_group_id = var.app_sg_id      # allow App SG
  }
  # outbound: allow all
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = {
    Name = "${var.env}-rds-sg"                   # change env as needed
    Env  = var.env
  }
}

# RDS Instance
resource "aws_db_instance" "this" {
  identifier              = "${var.env}-rds"             # change env as needed
  engine                  = var.engine                   # e.g., "mysql" or "postgres"
  engine_version          = var.engine_version           # optional, e.g., "8.0.33"
  instance_class          = var.instance_class           # e.g., "db.t3.medium"
  allocated_storage       = var.allocated_storage        # in GB
  storage_type            = var.storage_type             # gp3, gp2, etc.
  db_subnet_group_name    = aws_db_subnet_group.this.name
  vpc_security_group_ids  = [aws_security_group.rds_sg.id]
  multi_az                = var.multi_az                 # high availability
  publicly_accessible     = var.publicly_accessible      # usually false for DB
  username                = var.db_username              # master user
  password                = var.db_password              # master password
  skip_final_snapshot     = var.skip_final_snapshot      # change as needed
  backup_retention_period = var.backup_retention_period  # in days
  tags = {
    Name = "${var.env}-rds"                            # change env as needed
    Env  = var.env
  }
}

Example: modules/rds/variables.tf

# VPC ID
variable "vpc_id" {
  type = string  # pass VPC ID
}

# Private DB subnet IDs
variable "db_subnets" {
  type = list(string)  # private subnets for RDS
}

# App SG ID (to allow inbound DB access)
variable "app_sg_id" {
  type = string
}

# Environment name
variable "env" {
  type    = string
  default = "sendbox"  # change to prod/dev
}

# RDS engine and version
variable "engine" {
  type    = string
  default = "mysql"  # change as needed
}
variable "engine_version" {
  type    = string
  default = "8.0.33"  # change as needed
}

# RDS instance class
variable "instance_class" {
  type    = string
  default = "db.t3.medium"  # change as needed
}

# Storage configuration
variable "allocated_storage" {
  type    = number
  default = 20  # GB, change as needed
}
variable "storage_type" {
  type    = string
  default = "gp3"  # change as needed
}

# Multi-AZ
variable "multi_az" {
  type    = bool
  default = false  # set true for HA
}

# Public access
variable "publicly_accessible" {
  type    = bool
  default = false  # usually false
}

# DB credentials
variable "db_username" {
  type    = string
  default = "admin"  # change as needed
}
variable "db_password" {
  type      = string
  sensitive = true
}

# Backup retention
variable "backup_retention_period" {
  type    = number
  default = 7  # days
}

# Skip final snapshot on destroy
variable "skip_final_snapshot" {
  type    = bool
  default = true
}

Example: modules/rds/outputs.tf

# RDS instance ID
output "rds_instance_id" {
  value = aws_db_instance.this.id
}

# RDS endpoint
output "rds_endpoint" {
  value = aws_db_instance.this.endpoint
}

# RDS security group ID
output "rds_sg_id" {
  value = aws_security_group.rds_sg.id
}

# DB subnet group
output "rds_subnet_group_name" {
  value = aws_db_subnet_group.this.name
}

What to change
  • env → environment name (prod, dev, etc.).
  • vpc_id → VPC ID where RDS will reside.
  • db_subnets → private subnet IDs for RDS.
  • app_sg_id → Security Group ID for your application servers to allow inbound DB traffic.
  • engine / engine_version → RDS engine type and version.
  • instance_class → instance type (e.g., db.t3.medium).
  • allocated_storage / storage_type → storage size and type.
  • multi_az → true if high availability required.
  • publicly_accessible → false for private DB.
  • db_username / db_password → master credentials.
  • backup_retention_period → number of backup days.
  • skip_final_snapshot → true or false on destroy.
========================================================================================================================
Example: modules/dns/main.tf

# Route 53 Hosted Zone
resource "aws_route53_zone" "this" {
  name = var.domain_name                   # change domain name as needed
  comment = "${var.env} hosted zone"      # change env as needed
  tags = {
    Name = "${var.env}-hosted-zone"       # change env as needed
    Env  = var.env
  }
}

# Optional Record Sets: A records pointing to ALB / NLB
resource "aws_route53_record" "app_a_record" {
  for_each = var.app_endpoints             # map of {name = target} 
  zone_id  = aws_route53_zone.this.id
  name     = each.key                       # subdomain name
  type     = "A"
  alias {
    name                   = each.value    # DNS name of ALB/NLB
    zone_id                = var.endpoint_zone_ids[each.key]  # ALB/NLB zone ID
    evaluate_target_health = true
  }
}

# Optional Record Sets: CNAME for external endpoints
resource "aws_route53_record" "app_cname_record" {
  for_each = var.cname_endpoints          # map of {name = value}
  zone_id  = aws_route53_zone.this.id
  name     = each.key
  type     = "CNAME"
  ttl      = 300                           # change TTL if needed
  records  = [each.value]
}

Example: modules/dns/variables.tf

# Environment name
variable "env" {
  type    = string
  default = "sendbox"                      # change environment name
}

# Domain name for hosted zone
variable "domain_name" {
  type = string                             # e.g., "example.com"
}

# Map of subdomain → target for ALB/NLB alias A records
variable "app_endpoints" {
  type    = map(string)
  default = {}                              # example: { "app" = module.alb.alb_dns_name }
}

# Map of subdomain → zone ID for ALB/NLB alias record
variable "endpoint_zone_ids" {
  type    = map(string)
  default = {}                              # example: { "app" = module.alb.alb_zone_id }
}

# Map of subdomain → CNAME value
variable "cname_endpoints" {
  type    = map(string)
  default = {}                              # optional CNAMEs
}

Example: modules/dns/outputs.tf

# Hosted Zone ID
output "hosted_zone_id" {
  value = aws_route53_zone.this.id
}

# Hosted Zone Name
output "hosted_zone_name" {
  value = aws_route53_zone.this.name
}

# A Record FQDNs
output "a_record_fqdns" {
  value = [for r in aws_route53_record.app_a_record : r.fqdn]
}

# CNAME Record FQDNs
output "cname_record_fqdns" {
  value = [for r in aws_route53_record.app_cname_record : r.fqdn]
}

What to change
  • env → environment name for tags (prod, dev, etc.)
  • domain_name → your domain for the hosted zone (example.com)
  • app_endpoints → map of subdomain → ALB/NLB DNS names
  • endpoint_zone_ids → map of subdomain → ALB/NLB zone IDs (required for alias records)
  • cname_endpoints → optional subdomain → external CNAME targets
  • ttl → for CNAME records (default 300 seconds)
========================================================================================================================
Example: modules/iam/main.tf

# IAM Role
resource "aws_iam_role" "this" {
  name               = "${var.env}-${var.role_name}"        # change env or role name
  assume_role_policy = var.assume_role_policy              # JSON policy defining who can assume
  tags = {
    Name = "${var.env}-${var.role_name}"                  # change env or role name
    Env  = var.env
  }
}

# IAM Policy (optional inline)
resource "aws_iam_policy" "this" {
  count = var.inline_policy != "" ? 1 : 0
  name  = "${var.env}-${var.role_name}-policy"            # change env or policy name
  policy = var.inline_policy                               # JSON policy document
  tags = {
    Name = "${var.env}-${var.role_name}-policy"
    Env  = var.env
  }
}

# Attach policy to role (optional)
resource "aws_iam_role_policy_attachment" "attach" {
  count      = var.inline_policy != "" ? 1 : 0
  role       = aws_iam_role.this.name
  policy_arn = aws_iam_policy.this[0].arn
}

# Attach managed policies (optional list of ARNs)
resource "aws_iam_role_policy_attachment" "managed" {
  for_each = toset(var.managed_policy_arns)
  role      = aws_iam_role.this.name
  policy_arn = each.value
}

Example: modules/iam/variables.tf

# Environment name
variable "env" {
  type    = string
  default = "sendbox"                     # change environment name
}

# Role name
variable "role_name" {
  type = string                            # e.g., "flowlogs-role"
}

# JSON assume role policy
variable "assume_role_policy" {
  type = string
  description = "JSON policy defining who can assume this role"
}

# Optional inline policy JSON
variable "inline_policy" {
  type    = string
  default = ""                             # leave empty if not needed
}

# Optional list of managed policy ARNs to attach
variable "managed_policy_arns" {
  type    = list(string)
  default = []                             # e.g., ["arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"]
}

Example: modules/iam/outputs.tf

# IAM Role ARN
output "role_arn" {
  value = aws_iam_role.this.arn
}

# IAM Role Name
output "role_name" {
  value = aws_iam_role.this.name
}

# Inline Policy ARN (if created)
output "inline_policy_arn" {
  value = length(aws_iam_policy.this) > 0 ? aws_iam_policy.this[0].arn : ""
}

# List of Managed Policy ARNs attached
output "managed_policy_arns_attached" {
  value = [for a in aws_iam_role_policy_attachment.managed : a.policy_arn]
}

What to change
  • env → environment name for tags (prod, dev, etc.)
  • role_name → descriptive name for the role (e.g., flowlogs-role)
  • assume_role_policy → JSON defining who can assume the role (e.g., EC2, Lambda, Flow Logs)
  • inline_policy → optional inline JSON policy for the role
  • managed_policy_arns → optional list of AWS-managed policy ARNs to attach
========================================================================================================================
Example: modules/s3/main.tf

# S3 Bucket
resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name                # change bucket name as needed
  acl    = var.acl                        # e.g., "private", "public-read"
  tags = {
    Name = "${var.env}-${var.bucket_name}" # change env as needed
    Env  = var.env
  }
}

# Enable versioning (optional)
resource "aws_s3_bucket_versioning" "this" {
  bucket = aws_s3_bucket.this.id
  versioning_configuration {
    status = var.versioning ? "Enabled" : "Suspended"  # enable or disable
  }
}

# Enable server-side encryption (optional)
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
  count  = var.enable_encryption ? 1 : 0
  bucket = aws_s3_bucket.this.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = var.encryption_algorithm  # e.g., "AES256" or "aws:kms"
      kms_master_key_id = var.kms_key_id        # optional if using KMS
    }
  }
}

# Optional bucket policy
resource "aws_s3_bucket_policy" "this" {
  count  = var.bucket_policy != "" ? 1 : 0
  bucket = aws_s3_bucket.this.id
  policy = var.bucket_policy                  # JSON bucket policy
}

Example: modules/s3/variables.tf

# Environment name
variable "env" {
  type    = string
  default = "sendbox"                       # change environment name
}

# Bucket name (must be globally unique)
variable "bucket_name" {
  type = string
}

# ACL for bucket
variable "acl" {
  type    = string
  default = "private"                        # e.g., "private", "public-read"
}

# Enable versioning
variable "versioning" {
  type    = bool
  default = true
}

# Enable server-side encryption
variable "enable_encryption" {
  type    = bool
  default = true
}

# Encryption algorithm
variable "encryption_algorithm" {
  type    = string
  default = "AES256"                         # "AES256" or "aws:kms"
}

# Optional KMS key for encryption
variable "kms_key_id" {
  type    = string
  default = ""                               # leave empty if not using KMS
}

# Optional bucket policy JSON
variable "bucket_policy" {
  type    = string
  default = ""                               # leave empty if not needed
}

Example: modules/s3/outputs.tf

# Bucket ARN
output "bucket_arn" {
  value = aws_s3_bucket.this.arn
}

# Bucket Name
output "bucket_name" {
  value = aws_s3_bucket.this.bucket
}

# Versioning status
output "versioning_status" {
  value = aws_s3_bucket_versioning.this.status
}

# Encryption enabled (true/false)
output "encryption_enabled" {
  value = var.enable_encryption
}

What to change
  • env → environment name for tags (prod, dev, etc.)
  • bucket_name → globally unique S3 bucket name
  • acl → bucket ACL (default private)
  • versioning → enable or disable versioning (true/false)
  • enable_encryption → enable server-side encryption
  • encryption_algorithm → "AES256" or "aws:kms"
  • kms_key_id → optional KMS key ID if using KMS
  • bucket_policy → optional JSON policy for custom access rules
========================================================================================================================
Example: modules/secrets/main.tf

# Secrets Manager secret
resource "aws_secretsmanager_secret" "this" {
  name        = var.secret_name                    # change secret name
  description = var.description
  tags = {
    Name = "${var.env}-${var.secret_name}"        # change env as needed
    Env  = var.env
  }
}

# Secret value
resource "aws_secretsmanager_secret_version" "this" {
  secret_id     = aws_secretsmanager_secret.this.id
  secret_string = var.secret_value                # actual secret value
}

# Optional: SSM Parameter for secret reference
resource "aws_ssm_parameter" "this" {
  count      = var.create_ssm_parameter ? 1 : 0
  name       = var.ssm_name
  description = var.description
  type       = var.ssm_type                        # String, SecureString, StringList
  value      = var.secret_value
  overwrite  = true
  tags = {
    Name = "${var.env}-${var.ssm_name}"           # change env as needed
    Env  = var.env
  }
}

Example: modules/secrets/variables.tf

# Environment name
variable "env" {
  type    = string
  default = "sendbox"                       # change environment name
}

# Secrets Manager secret name
variable "secret_name" {
  type = string
}

# Secret value (JSON or string)
variable "secret_value" {
  type = string
}

# Optional description
variable "description" {
  type    = string
  default = "Managed by Terraform"
}

# Create optional SSM parameter
variable "create_ssm_parameter" {
  type    = bool
  default = false
}

# SSM parameter name (optional)
variable "ssm_name" {
  type    = string
  default = ""
}

# SSM parameter type: String, SecureString, StringList
variable "ssm_type" {
  type    = string
  default = "SecureString"
}

Example: modules/secrets/outputs.tf

# Secrets Manager Secret ARN
output "secret_arn" {
  value = aws_secretsmanager_secret.this.arn
}

# Secrets Manager Secret Name
output "secret_name" {
  value = aws_secretsmanager_secret.this.name
}

# SSM Parameter Name (if created)
output "ssm_name" {
  value = var.create_ssm_parameter ? aws_ssm_parameter.this[0].name : ""
}

# SSM Parameter ARN (if created)
output "ssm_arn" {
  value = var.create_ssm_parameter ? aws_ssm_parameter.this[0].arn : ""
}

What to change
  • env → environment name for tags (prod, dev, etc.)
  • secret_name → Secrets Manager secret name
  • secret_value → actual secret content (JSON or string)
  • description → optional description of the secret
  • create_ssm_parameter → whether to create a corresponding SSM Parameter
  • ssm_name → name of SSM Parameter (if used)
  • ssm_type → String, SecureString, or StringList
========================================================================================================================
Example: modules/cloudwatch-alarms/main.tf

# CloudWatch Alarm for CPU Utilization
resource "aws_cloudwatch_metric_alarm" "cpu" {
  alarm_name          = "${var.env}-${var.resource_name}-cpu"  # change environment name and resource
  comparison_operator = var.cpu_comparison_operator            # e.g., GreaterThanThreshold
  evaluation_periods  = var.cpu_evaluation_periods
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = var.cpu_period
  statistic           = var.cpu_statistic
  threshold           = var.cpu_threshold
  alarm_description   = var.description
  alarm_actions       = var.alarm_actions                      # SNS topic ARNs or ARNs of actions
  dimensions          = var.dimensions
  treat_missing_data  = "missing"

  tags = {
    Name = "${var.env}-${var.resource_name}-cpu"             # change environment name and resource
    Env  = var.env                                           # change environment name
  }
}

# Optional CloudWatch Alarm for Memory Utilization (requires custom metric)
resource "aws_cloudwatch_metric_alarm" "memory" {
  count               = var.enable_memory_alarm ? 1 : 0
  alarm_name          = "${var.env}-${var.resource_name}-memory"  # change environment name and resource
  comparison_operator = var.memory_comparison_operator
  evaluation_periods  = var.memory_evaluation_periods
  metric_name         = "MemoryUtilization"
  namespace           = var.memory_namespace
  period              = var.memory_period
  statistic           = var.memory_statistic
  threshold           = var.memory_threshold
  alarm_description   = var.description
  alarm_actions       = var.alarm_actions
  dimensions          = var.dimensions
  treat_missing_data  = "missing"

  tags = {
    Name = "${var.env}-${var.resource_name}-memory"       # change environment name and resource
    Env  = var.env                                         # change environment name
  }
}

Example: modules/cloudwatch-alarms/variables.tf

# Environment name
variable "env" {
  type    = string
  default = "sendbox"  # change environment name (e.g., "prod", "dev", "staging")
}

# Resource identifier (EC2, RDS, etc.)
variable "resource_name" {
  type = string  # change to identify your resource
}

# Alarm actions (SNS topic ARNs or other ARNs)
variable "alarm_actions" {
  type    = list(string)
  default = []
}

# Dimensions for the metric (e.g., {"InstanceId" = "i-123456"})
variable "dimensions" {
  type    = map(string)
  default = {}
}

# Optional description
variable "description" {
  type    = string
  default = "CloudWatch Alarm managed by Terraform"
}

# CPU Alarm variables
variable "cpu_comparison_operator" {
  type    = string
  default = "GreaterThanThreshold"
}
variable "cpu_evaluation_periods" {
  type    = number
  default = 3
}
variable "cpu_period" {
  type    = number
  default = 300
}
variable "cpu_statistic" {
  type    = string
  default = "Average"
}
variable "cpu_threshold" {
  type    = number
  default = 80
}

# Memory Alarm variables
variable "enable_memory_alarm" {
  type    = bool
  default = false
}
variable "memory_comparison_operator" {
  type    = string
  default = "GreaterThanThreshold"
}
variable "memory_evaluation_periods" {
  type    = number
  default = 3
}
variable "memory_period" {
  type    = number
  default = 300
}
variable "memory_statistic" {
  type    = string
  default = "Average"
}
variable "memory_threshold" {
  type    = number
  default = 80
}
variable "memory_namespace" {
  type    = string
  default = "System/Linux"
}

Example: modules/cloudwatch-alarms/outputs.tf

# CPU Alarm ARN
output "cpu_alarm_arn" {
  value = aws_cloudwatch_metric_alarm.cpu.arn
}

# Memory Alarm ARN (if created)
output "memory_alarm_arn" {
  value = var.enable_memory_alarm ? aws_cloudwatch_metric_alarm.memory[0].arn : ""
}

Where to change:
  • env → environment name for tags and alarm names (# change environment name)
  • resource_name → resource identifier (# change to identify your resource)
  • alarm_actions → SNS topic or other action ARNs
  • dimensions → resource identifiers (EC2 instance ID, RDS instance, etc.)
========================================================================================================================
Example: modules/bastion/main.tf

# Bastion / Jump Box EC2 Instance
resource "aws_instance" "bastion" {
  ami                         = var.ami_id                     # change AMI ID
  instance_type               = var.instance_type              # change instance type
  subnet_id                   = var.subnet_id                  # change subnet ID
  key_name                    = var.key_name                   # change SSH key name
  associate_public_ip_address = var.associate_public_ip        # true if public bastion

  vpc_security_group_ids      = [aws_security_group.bastion.id]
  tags = {
    Name = "${var.env}-${var.resource_name}-bastion"         # change environment name and resource
    Env  = var.env                                           # change environment name
  }
}

# Security Group for Bastion
resource "aws_security_group" "bastion" {
  name        = "${var.env}-${var.resource_name}-bastion-sg"  # change environment name and resource
  description = "Security group for Bastion host"
  vpc_id      = var.vpc_id                                    # change VPC ID

  # Allow SSH from trusted IPs
  ingress {
    description      = "SSH from trusted IPs"
    from_port        = 22
    to_port          = 22
    protocol         = "tcp"
    cidr_blocks      = var.allowed_ips
  }

  # Allow all outbound (optional)
  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  tags = {
    Name = "${var.env}-${var.resource_name}-bastion-sg"    # change environment name and resource
    Env  = var.env                                         # change environment name
  }
}

Example: modules/bastion/variables.tf

# Environment name
variable "env" {
  type    = string
  default = "sendbox"   # change environment name
}

# Resource name identifier
variable "resource_name" {
  type = string       # change resource name
}

# VPC and subnet
variable "vpc_id" {
  type = string       # change VPC ID
}
variable "subnet_id" {
  type = string       # change subnet ID
}

# EC2 configuration
variable "ami_id" {
  type = string       # change AMI ID (Linux recommended)
}
variable "instance_type" {
  type    = string
  default = "t3.micro"   # change instance type if needed
}
variable "key_name" {
  type = string       # change SSH key name
}
variable "associate_public_ip" {
  type    = bool
  default = true      # true if bastion should be public
}

# Allowed IPs for SSH
variable "allowed_ips" {
  type    = list(string)
  default = ["0.0.0.0/0"]  # change to your trusted IPs
}

Example: modules/bastion/outputs.tf

# Bastion public IP
output "bastion_public_ip" {
  value = aws_instance.bastion.public_ip
}

# Bastion private IP
output "bastion_private_ip" {
  value = aws_instance.bastion.private_ip
}

# Security group ID
output "bastion_sg_id" {
  value = aws_security_group.bastion.id
}
Example Usage
module "bastion" {
  source        = "./modules/bastion"
  env           = "prod"                                # change environment name
  resource_name = "jumpbox"                              # change resource name

  vpc_id        = aws_vpc.main.id                        # change VPC
  subnet_id     = aws_subnet.public.id                   # change subnet
  ami_id        = "ami-0abcdef1234567890"               # change AMI
  instance_type = "t3.micro"                             # change if needed
  key_name      = "my-key"                               # change SSH key name
  associate_public_ip = true

  allowed_ips   = ["1.2.3.4/32"]                         # change to trusted IPs
}

Where to change:
  • env → environment name for tags (prod, dev, etc.)
  • resource_name → name of the bastion/jump box
  • vpc_id / subnet_id → VPC and subnet for the bastion
  • ami_id → Linux AMI ID for your region
  • instance_type → EC2 instance type
  • key_name → SSH key name for access
  • allowed_ips → trusted IPs allowed to SSH
========================================================================================================================
Example: modules/vpc-connection/main.tf

# VPC Peering Connection
resource "aws_vpc_peering_connection" "peer" {
  count = var.connection_type == "vpc_peering" ? 1 : 0

  vpc_id        = var.vpc_id                     # change source VPC ID
  peer_vpc_id   = var.peer_vpc_id                # change target VPC ID
  peer_region   = var.peer_region                # change target VPC region if cross-region
  auto_accept   = var.auto_accept                # true if automatic acceptance

  tags = {
    Name = "${var.env}-${var.resource_name}-vpc-peering"  # change environment name and resource
    Env  = var.env                                         # change environment name
  }
}

# VPC Peering Route Table Associations (optional)
resource "aws_route" "peer_routes" {
  count              = var.connection_type == "vpc_peering" ? length(var.route_table_ids) : 0
  route_table_id     = var.route_table_ids[count.index]   # change your route table IDs
  destination_cidr_block = var.peer_cidr_block           # target VPC CIDR
  vpc_peering_connection_id = aws_vpc_peering_connection.peer[0].id
}

# VPN Connection (for remote on-prem / transit)
resource "aws_vpn_connection" "vpn" {
  count              = var.connection_type == "vpn" ? 1 : 0

  customer_gateway_id = var.customer_gateway_id         # change customer gateway ID
  vpn_gateway_id      = var.vpn_gateway_id              # change VPN gateway ID
  type                = var.vpn_type                    # e.g., "ipsec.1"
  static_routes_only  = var.static_routes_only         # true/false
  enable_acceleration = var.enable_acceleration

  tags = {
    Name = "${var.env}-${var.resource_name}-vpn"      # change environment name and resource
    Env  = var.env                                     # change environment name
  }
}

# Optional VPN Routes
resource "aws_vpn_connection_route" "vpn_routes" {
  count                  = var.connection_type == "vpn" ? length(var.vpn_routes) : 0
  vpn_connection_id      = aws_vpn_connection.vpn[0].id
  destination_cidr_block = var.vpn_routes[count.index]   # change destination CIDRs
}

Example: modules/vpc-connection/variables.tf

# Environment name
variable "env" {
  type    = string
  default = "sendbox"   # change environment name (prod, dev, etc.)
}

# Resource identifier
variable "resource_name" {
  type = string       # change resource name
}

# Connection type: "vpc_peering" or "vpn"
variable "connection_type" {
  type    = string
  default = "vpc_peering"
}

# VPC Peering specific
variable "vpc_id" {
  type = string       # change source VPC ID
}
variable "peer_vpc_id" {
  type = string       # change target VPC ID
}
variable "peer_region" {
  type    = string
  default = ""        # optional for same-region peering
}
variable "auto_accept" {
  type    = bool
  default = true
}
variable "route_table_ids" {
  type    = list(string)
  default = []        # list of route table IDs to add peering routes
}
variable "peer_cidr_block" {
  type = string       # target VPC CIDR for route
}

# VPN specific
variable "customer_gateway_id" {
  type    = string
  default = ""        # change customer gateway ID for VPN
}
variable "vpn_gateway_id" {
  type    = string
  default = ""        # change VPN gateway ID
}
variable "vpn_type" {
  type    = string
  default = "ipsec.1"
}
variable "static_routes_only" {
  type    = bool
  default = true
}
variable "enable_acceleration" {
  type    = bool
  default = false
}
variable "vpn_routes" {
  type    = list(string)
  default = []        # list of destination CIDRs for VPN
}

Example: modules/vpc-connection/outputs.tf

# VPC Peering ID
output "vpc_peering_id" {
  value = var.connection_type == "vpc_peering" ? aws_vpc_peering_connection.peer[0].id : ""
}

# VPN Connection ID
output "vpn_connection_id" {
  value = var.connection_type == "vpn" ? aws_vpn_connection.vpn[0].id : ""
}
Example Usage for VPC Peering
module "vpc_peering" {
  source        = "./modules/vpc-connection"
  env           = "prod"                         # change environment name
  resource_name = "app-vpc-peer"                 # change resource name
  connection_type = "vpc_peering"

  vpc_id          = aws_vpc.app.id               # source VPC
  peer_vpc_id     = aws_vpc.shared.id            # target VPC
  peer_region     = "us-east-1"                  # optional
  auto_accept     = true
  route_table_ids = [aws_route_table.app.id]     # route tables to update
  peer_cidr_block = "10.1.0.0/16"               # target VPC CIDR
}
Example Usage for VPN
module "vpn_connection" {
  source        = "./modules/vpc-connection"
  env           = "prod"                         # change environment name
  resource_name = "app-vpn"                      # change resource name
  connection_type = "vpn"

  customer_gateway_id = aws_customer_gateway.onprem.id
  vpn_gateway_id      = aws_vpn_gateway.app.id
  vpn_type            = "ipsec.1"
  static_routes_only  = true
  enable_acceleration = false
  vpn_routes          = ["10.1.0.0/16", "10.2.0.0/16"]
}

Where to change:
  • env → environment name (tags & resource names)
  • resource_name → module resource identifier
  • VPC Peering: vpc_id, peer_vpc_id, peer_region, route_table_ids, peer_cidr_block
  • VPN: customer_gateway_id, vpn_gateway_id, vpn_routes
========================================================================================================================

Folder Structure:
envs/project-template/
├── main.tf
├── variables.tf
├── terraform.tfvars
├── backend.tf
└── scripts/
    └── pacemaker_setup.sh

Example: envs/project-template/main.tf

provider "aws" {
  region = var.aws_region
}

########################
# <<< CHANGE ENVIRONMENT NAME HERE >>>
# var.env controls naming, tags, cluster names, alarms
########################

# Network Module
module "network" {
  source         = "../../modules/network"
  vpc_cidr       = var.vpc_cidr
  public_subnets = var.public_subnets
  app_subnets    = var.app_subnets
  db_subnets     = var.db_subnets
  azs            = var.azs
  env            = var.env
}

# NACL Module
module "nacl" {
  source         = "../../modules/nacl"
  vpc_id         = module.network.vpc_id
  public_subnets = module.network.public_subnets
  app_subnets    = module.network.app_subnets
  db_subnets     = module.network.db_subnets
  internal_cidr  = var.vpc_cidr
  env            = var.env
}

# Inspection VPC
module "inspection_vpc" {
  source          = "../../modules/inspection-vpc"
  vpc_cidr        = var.inspection_vpc_cidr
  public_subnets  = var.inspection_public_subnets
  private_subnets = var.inspection_private_subnets
  azs             = var.azs
  env             = var.env
}

# TGW Module
module "tgw" {
  source = "../../modules/tgw"
  env    = var.env
  vpc_attachments = var.tgw_vpc_attachments
  route_table_id  = var.tgw_route_table_id
  enable_peering  = var.tgw_enable_peering
  peer_tgw_id     = var.peer_tgw_id
  peer_account_id = var.peer_account_id
  peer_region     = var.peer_region
}

# Flow Logs
module "flowlogs" {
  source       = "../../modules/flowlogs"
  vpc_id       = module.network.vpc_id
  iam_role_arn = var.flowlogs_iam_role_arn
  env          = var.env
}

# Security Groups
module "sg" {
  source             = "../../modules/security-groups"
  vpc_id             = module.network.vpc_id
  public_cidr_blocks = var.public_cidr_blocks
  env                = var.env
}

# ALB
module "alb" {
  source          = "../../modules/alb"
  vpc_id          = module.network.vpc_id
  public_subnets  = module.network.public_subnets
  web_sg_id       = module.sg.web_sg_id
  certificate_arn = var.acm_certificate_arn
  waf_acl_id      = var.waf_acl_id
  env             = var.env
}

# NLB
module "nlb" {
  source        = "../../modules/nlb"
  vpc_id        = module.network.vpc_id
  subnets       = module.network.app_subnets
  listener_port = var.nlb_listener_port
  target_port   = var.nlb_target_port
  internal      = var.nlb_internal
  env           = var.env
}

# Compute
module "compute" {
  source            = "../../modules/compute"
  vpc_id            = module.network.vpc_id
  app_subnets       = module.network.app_subnets
  db_subnets        = module.network.db_subnets
  web_sg_id         = module.sg.web_sg_id
  app_sg_id         = module.sg.app_sg_id
  db_sg_id          = module.sg.db_sg_id
  ami_id            = var.golden_ami_id
  instance_type     = var.instance_type
  key_name          = var.key_name
  asg_min_size      = var.asg_min_size
  asg_max_size      = var.asg_max_size
  asg_desired_count = var.asg_desired_count
  env               = var.env
}

# Bastion / Jump Box
module "bastion" {
  source             = "../../modules/bastion"
  env                = var.env
  resource_name      = "bastion"
  vpc_id             = module.network.vpc_id
  subnet_id          = module.network.public_subnets[0]
  ami_id             = var.bastion_ami_id
  instance_type      = var.bastion_instance_type
  key_name           = var.key_name
  associate_public_ip = true
  allowed_ips        = var.bastion_allowed_ips
}

# CloudWatch Alarms (Compute & Bastion)
module "cloudwatch_alarms_compute" {
  source             = "../../modules/cloudwatch-alarms"
  env                = var.env
  resource_name      = "compute"
  alarm_actions      = var.cloudwatch_sns_arns
  dimensions         = { InstanceId = module.compute.app_instance_ids[0] }
  enable_memory_alarm = true
}

module "cloudwatch_alarms_bastion" {
  source             = "../../modules/cloudwatch-alarms"
  env                = var.env
  resource_name      = "bastion"
  alarm_actions      = var.cloudwatch_sns_arns
  dimensions         = { InstanceId = module.bastion.instance_id }
  enable_memory_alarm = false
}

# VPC Peering / VPN
module "vpc_connection" {
  source          = "../../modules/vpc-connection"
  env             = var.env
  resource_name   = "peer"
  connection_type = var.vpc_connection_type
  vpc_id          = module.network.vpc_id
  peer_vpc_id     = var.peer_vpc_id
  peer_region     = var.peer_region
  auto_accept     = true
  route_table_ids = var.peer_route_table_ids
  peer_cidr_block = var.peer_cidr_block
}

# Pacemaker HA
module "pacemaker" {
  source               = "../../modules/pacemaker"
  vpc_id               = module.network.vpc_id
  private_subnets      = module.network.app_subnets
  ami_id               = var.golden_ami_id
  instance_type        = var.instance_type
  key_name             = var.key_name
  internal_cidr_blocks = [var.vpc_cidr]
  ssh_cidr_blocks      = var.ssh_cidr_blocks
  user_data_script     = var.pacemaker_user_data
  env                  = var.env
}

Example: envs/project-template/variables.tf

############################
# Core Environment
############################
variable "aws_region" {
  type    = string
  default = "us-east-1"             # <<< CHANGE HERE if region differs
}

variable "env" {
  type    = string
  default = "sendbox"               # <<< CHANGE HERE for environment name (dev/qa/prod)
}

############################
# Remote State
############################
variable "tfstate_bucket" {
  type        = string
  description = "S3 bucket for remote Terraform state"
}

variable "tfstate_lock_table" {
  type        = string
  description = "DynamoDB table for state locking"
}

############################
# Networking
############################
variable "vpc_cidr" {
  type    = string
  default = "10.0.0.0/16"          # <<< CHANGE HERE for VPC CIDR
}

variable "public_subnets" {
  type    = list(string)
  default = ["10.0.1.0/24","10.0.2.0/24"]  # <<< CHANGE HERE
}

variable "app_subnets" {
  type    = list(string)
  default = ["10.0.11.0/24","10.0.12.0/24"]  # <<< CHANGE HERE
}

variable "db_subnets" {
  type    = list(string)
  default = ["10.0.21.0/24","10.0.22.0/24"]  # <<< CHANGE HERE
}

variable "azs" {
  type    = list(string)
  default = ["us-east-1a","us-east-1b"]  # <<< CHANGE HERE
}

############################
# Inspection VPC
############################
variable "inspection_vpc_cidr" {
  type    = string
  default = "10.100.0.0/16"          # <<< CHANGE HERE
}

variable "inspection_public_subnets" {
  type    = list(string)
  default = ["10.100.1.0/24","10.100.2.0/24"]  # <<< CHANGE HERE
}

variable "inspection_private_subnets" {
  type    = list(string)
  default = ["10.100.11.0/24","10.100.12.0/24"]  # <<< CHANGE HERE
}

############################
# Transit Gateway
############################
variable "tgw_route_table_id" {
  type    = string
  default = ""                      # <<< CHANGE HERE
}

variable "tgw_vpc_attachments" {
  type    = map(any)
  default = {}                       # <<< CHANGE HERE per project
}

variable "tgw_enable_peering" {
  type    = bool
  default = false                    # <<< CHANGE HERE if TGW peering required
}

variable "peer_tgw_id" {
  type    = string
  default = ""                        # <<< CHANGE HERE if TGW peering
}

variable "peer_account_id" {
  type    = string
  default = ""                        # <<< CHANGE HERE if TGW peering
}

variable "peer_region" {
  type    = string
  default = ""                        # <<< CHANGE HERE if TGW peering
}

############################
# Flow Logs
############################
variable "flowlogs_iam_role_arn" {
  type    = string
  default = ""                        # <<< CHANGE HERE
}

############################
# Security Groups
############################
variable "public_cidr_blocks" {
  type    = list(string)
  default = ["0.0.0.0/0"]             # <<< CHANGE HERE
}

############################
# ALB / WAF / ACM
############################
variable "acm_certificate_arn" {
  type    = string
  default = ""                        # <<< CHANGE HERE
}

variable "waf_acl_id" {
  type    = string
  default = ""                        # <<< CHANGE HERE
}

############################
# NLB
############################
variable "nlb_listener_port" {
  type    = number
  default = 3306                     # <<< CHANGE HERE
}

variable "nlb_target_port" {
  type    = number
  default = 3306                     # <<< CHANGE HERE
}

variable "nlb_internal" {
  type    = bool
  default = true                     # <<< CHANGE HERE
}

############################
# Compute (EC2 + ASG)
############################
variable "golden_ami_id" {
  type    = string
  default = ""                        # <<< CHANGE HERE
}

variable "instance_type" {
  type    = string
  default = "t3.medium"              # <<< CHANGE HERE
}

variable "key_name" {
  type    = string
  default = ""                        # <<< CHANGE HERE
}

variable "asg_min_size" {
  type    = number
  default = 1
}

variable "asg_max_size" {
  type    = number
  default = 3
}

variable "asg_desired_count" {
  type    = number
  default = 2
}

############################
# Bastion / Jump Box
############################
variable "bastion_ami_id" {
  type    = string
  default = ""                        # <<< CHANGE HERE
}

variable "bastion_instance_type" {
  type    = string
  default = "t3.micro"               # <<< CHANGE HERE
}

variable "bastion_allowed_ips" {
  type    = list(string)
  default = ["0.0.0.0/0"]            # <<< CHANGE HERE
}

############################
# CloudWatch
############################
variable "cloudwatch_sns_arns" {
  type    = list(string)
  default = []                        # <<< CHANGE HERE
}

############################
# VPC Peering / VPN
############################
variable "vpc_connection_type" {
  type    = string
  default = "vpc_peering"            # <<< CHANGE HERE if using VPN
}

variable "peer_vpc_id" {
  type    = string
  default = ""                        # <<< CHANGE HERE
}

variable "peer_route_table_ids" {
  type    = list(string)
  default = []                        # <<< CHANGE HERE
}

variable "peer_cidr_block" {
  type    = string
  default = ""                        # <<< CHANGE HERE
}

############################
# Pacemaker HA
############################
variable "ssh_cidr_blocks" {
  type    = list(string)
  default = ["10.0.0.0/16"]           # <<< CHANGE HERE
}

variable "pacemaker_user_data" {
  type    = string
  default = "${path.module}/scripts/pacemaker_setup.sh"
}

Example: envs/project-template/terraform.tfvars

# <<< CHANGE ENVIRONMENT HERE >>>
env = "sendbox"                        # <<< CHANGE HERE for dev/qa/prod

aws_region = "us-east-1"               # <<< CHANGE HERE

# Remote state
tfstate_bucket     = "my-terraform-states"    # <<< CHANGE HERE
tfstate_lock_table = "terraform-locks"        # <<< CHANGE HERE

# Networking
vpc_cidr       = "10.0.0.0/16"                # <<< CHANGE HERE
public_subnets = ["10.0.1.0/24","10.0.2.0/24"] # <<< CHANGE HERE
app_subnets    = ["10.0.11.0/24","10.0.12.0/24"]
db_subnets     = ["10.0.21.0/24","10.0.22.0/24"]
azs            = ["us-east-1a","us-east-1b"]

# Inspection VPC
inspection_vpc_cidr        = "10.100.0.0/16"
inspection_public_subnets  = ["10.100.1.0/24","10.100.2.0/24"]
inspection_private_subnets = ["10.100.11.0/24","10.100.12.0/24"]

# TGW
tgw_route_table_id  = ""
tgw_vpc_attachments = {}        # Fill per project
tgw_enable_peering  = false
peer_tgw_id         = ""
peer_account_id     = ""
peer_region         = ""

# Flow Logs
flowlogs_iam_role_arn = ""

# Security Groups
public_cidr_blocks = ["0.0.0.0/0"]

# ALB/WAF/ACM
acm_certificate_arn = ""
waf_acl_id          = ""

# NLB
nlb_listener_port = 3306
nlb_target_port   = 3306
nlb_internal      = true

# Compute (EC2 + ASG)
golden_ami_id     = ""
instance_type     = "t3.medium"
key_name          = ""
asg_min_size      = 1
asg_max_size      = 3
asg_desired_count = 2

# Bastion
bastion_ami_id       = ""
bastion_instance_type = "t3.micro"
bastion_allowed_ips  = ["0.0.0.0/0"]

# CloudWatch
cloudwatch_sns_arns = []

# VPC Peering / VPN
vpc_connection_type  = "vpc_peering"
peer_vpc_id          = ""
peer_route_table_ids = []
peer_cidr_block      = ""

# Pacemaker HA
ssh_cidr_blocks      = ["10.0.0.0/16"]
pacemaker_user_data  = "${path.module}/scripts/pacemaker_setup.sh"

Example: envs/project-template/backend.tf

terraform {
  backend "s3" {
    bucket         = var.tfstate_bucket
    key            = "${var.env}/terraform.tfstate"   # <<< Automatically changes per environment
    region         = var.aws_region
    dynamodb_table = var.tfstate_lock_table
    encrypt        = true
  }
}

How to use:
Copy envs/project-template/ → envs/myproject.
Edit terraform.tfvars with your project-specific values (env name, VPC, AMIs, TGW, ALBs, Bastion, CloudWatch, etc.).
Optionally update variables defaults in variables.tf.
Deploy:
  • terraform init
  • terraform plan
  • terraform apply
Pacemaker script runs automatically on nodes for HA floating IPs.

========================================================================================================================
Example: global/accounts.tf – Hub-Spoke Multi-Account Mapping

# Example: Multi-account mapping for Hub-Spoke landing zone
# <<< CHANGE OR ADD ENVIRONMENTS HERE >>>
variable "accounts" {
  type = map(object({
    account_id   = string
    environment  = string
    role_name    = string
    region       = string
  }))
  default = {
    hub = {
      account_id  = "111111111111"                # <<< CHANGE HERE for Hub AWS account ID
      environment = "global"                     # <<< CHANGE HERE for Hub environment name
      role_name   = "OrganizationAccountAccessRole"  # <<< CHANGE HERE if role differs
      region      = "us-east-1"                  # <<< CHANGE HERE for Hub region
    }
    sendbox = {
      account_id  = "222222222222"               # <<< CHANGE HERE for Sandbox AWS account ID
      environment = "sendbox"                    # <<< CHANGE HERE for Sandbox environment name
      role_name   = "OrganizationAccountAccessRole"
      region      = "us-east-1"
    }
    qa = {
      account_id  = "333333333333"               # <<< CHANGE HERE for QA AWS account ID
      environment = "qa"                         # <<< CHANGE HERE for QA environment name
      role_name   = "OrganizationAccountAccessRole"
      region      = "us-east-1"
    }
    prod = {
      account_id  = "444444444444"               # <<< CHANGE HERE for Prod AWS account ID
      environment = "prod"                       # <<< CHANGE HERE for Prod environment name
      role_name   = "OrganizationAccountAccessRole"
      region      = "us-east-1"
    }
  }
}

# Output to share account mapping with other modules/envs
output "account_map" {
  value = var.accounts
}

Example: global/backend.tf – Shared Backend S3 + DynamoDB Lock

terraform {
  backend "s3" {
    bucket         = "my-terraform-states"      # <<< CHANGE HERE for S3 bucket
    key            = "global/terraform.tfstate" # <<< CHANGE HERE if needed per environment
    region         = "us-east-1"                # <<< CHANGE HERE for bucket region
    dynamodb_table = "terraform-locks"          # <<< CHANGE HERE for DynamoDB table
    encrypt        = true
  }
}

# Tip: To parameterize by environment
# key = "${var.environment}/terraform.tfstate"

Example: global/providers.tf – Shared AWS Provider

# AWS region for global resources
variable "aws_region" {
  type    = string
  default = "us-east-1"       # <<< CHANGE HERE for AWS region
}

# Optional role ARN for cross-account assume role
variable "assume_role_arn" {
  type    = string
  default = ""                # <<< CHANGE HERE if using cross-account deployment
}

provider "aws" {
  region = var.aws_region

  # Optional multi-account assume role
  assume_role {
    role_arn     = var.assume_role_arn
    session_name = "TerraformGlobal"
  }
}

Where to change per environment/account:
account_id: AWS account ID for that environment (e.g., Sandbox, QA, Prod).
environment: Name of the environment, used in resource naming and state paths.
role_name: IAM role you will assume in that account.
region: AWS region for that environment/account.
bucket/key: In backend.tf, change S3 bucket, key, region, or DynamoDB lock table if needed.

No comments:

Post a Comment