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:
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