Skip to content

phuru7/terraform-aws-ec2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

terraform-aws-ec2

Terraform module for creating EC2 instances with environment-based configuration, IMDSv2 security, flexible EBS volumes, elastic IPs, and automatic resource management.

Features

  • IMDSv2 Security: Instance Metadata Service v2 enabled by default for SSRF protection
  • Environment-based configuration: Predefined values for dev, qa, staging, prod
  • Automatic distribution: Instances distributed across multiple subnets
  • Flexible EBS volumes: Granular configuration with robust validations
  • Elastic IPs: Optional automatic assignment
  • Key pairs: Create new or use existing
  • Organized tags: Tagging system by resource type
  • Advanced validations: Configuration verification before deployment

Architecture

Environment → Instance Config → EC2 Instances (IMDSv2)
    ↓              ↓               ↓
Defaults    →  EBS Volumes  →  Auto Distribution
    ↓              ↓               ↓
Tags        →  Elastic IPs  →  Across Subnets

Requirements

  • Terraform ≥ 1.1
  • AWS Provider ~> 6.0
  • Configured VPC and subnets
  • Existing security groups

Basic Usage

module "app_servers" {
  source = "./terraform-aws-ec2"
  
  # Base configuration
  environment    = "prod"
  company_name   = "acme"
  project_name   = "webapp"
  
  # Network
  network_config = {
    subnet_ids         = ["subnet-12345", "subnet-67890"]
    security_group_ids = ["sg-abcdef"]
    eip_count          = 2
  }
  
  # Existing key pair
  key_pair_config = {
    create_new        = false
    existing_key_name = "my-existing-key"
  }
  
  # Security: IMDSv2 enabled by default
  metadata_options = {
    http_tokens = "required"  # Forces IMDSv2
  }
  
  tags = {
    Owner = "DevOps"
    Cost  = "Engineering"
  }
}

Security Configuration

IMDSv2 (Default)

metadata_options = {
  http_endpoint               = "enabled"   # Default
  http_tokens                = "required"   # Forces IMDSv2 (Default)
  http_put_response_hop_limit = 2           # For containers (Default)
  instance_metadata_tags      = "disabled"  # Default
}

Custom Security Settings

module "secure_servers" {
  source = "./terraform-aws-ec2"
  
  environment    = "prod"
  company_name   = "acme"
  project_name   = "secure-app"
  
  # Override default IMDSv2 settings if needed
  metadata_options = {
    http_tokens                = "required"
    http_put_response_hop_limit = 3  # For complex container setups
  }
  
  # Encrypted storage (default)
  root_volume = {
    encrypted = true  # Default
    type      = "gp3"
    size      = 100
  }
  
  ebs_volumes = {
    data = {
      device_name = "/dev/sdf"
      volume_size = 500
      encrypted   = true  # Default
    }
  }
}

Advanced Usage

module "database_servers" {
  source = "./terraform-aws-ec2"
  
  environment    = "prod"
  company_name   = "acme"
  project_name   = "database"
  
  # Override environment defaults
  instance_config = {
    instance_count  = 3
    instance_type   = "r6i.xlarge"
    monitoring      = true
  }
  
  # Custom root volume configuration
  root_volume = {
    type = "gp3"
    size = 200
    iops = 10000
  }
  
  # Additional EBS volumes
  ebs_volumes = {
    data = {
      device_name      = "/dev/sdf"
      volume_size      = 500
      volume_type      = "gp3"
      instance_indices = [0, 1, 2]
      tags = {
        Purpose = "Database Storage"
      }
    }
    logs = {
      device_name      = "/dev/sdg"
      volume_size      = 100
      instance_indices = [0, 1]
    }
  }
  
  network_config = {
    subnet_ids                  = ["subnet-db1", "subnet-db2", "subnet-db3"]
    security_group_ids          = ["sg-database"]
    associate_public_ip_address = false
    eip_count                   = 0
  }
  
  tags = {
    Environment = "production"
    Service     = "database"
    Backup      = "required"
  }
}

Environment Configurations

The module automatically applies optimized configurations per environment:

Environment Instance Type Monitoring Termination Protection Root Volume Instance Count
dev t3.micro false false 30 GB 1
qa t3.small true false 40 GB 2
staging t3.medium true true 50 GB 2
prod t3.large true true 100 GB 3

Main Variables

Required

environment    = "prod"           # dev, qa, staging, prod
company_name   = "acme"           # Company name (lowercase)
project_name   = "webapp"         # Project name (lowercase)

network_config = {
  subnet_ids         = ["subnet-xxx"]    # Minimum 1 subnet
  security_group_ids = ["sg-xxx"]        # Minimum 1 security group
}

Security (Optional)

metadata_options = {
  http_tokens                = "required"  # Forces IMDSv2 (default)
  http_put_response_hop_limit = 2          # Container support (default)
}

Main Optional

instance_config = {
  instance_count  = 2              # Override environment default
  instance_type   = "t3.medium"    # Override environment default
  monitoring      = true           # Override environment default
}

ami_config = {
  ami_id = "ami-12345"            # If not specified, uses Ubuntu 24.04 LTS
}

ebs_volumes = {
  volume_name = {
    device_name = "/dev/sdf"
    volume_size = 100
  }
}

Security Features

Default Security Implementations:

  • IMDSv2 required (prevents SSRF attacks)
  • EBS encryption enabled by default
  • Root volume encryption enabled
  • Security group restrictions (user-defined)
  • Instance termination protection (production environments)

Security Best Practices:

  • Never disable IMDSv2 in production
  • Use encrypted volumes for sensitive data
  • Configure security groups with least privilege
  • Enable monitoring in staging/production
  • Regular security group audits

Useful Outputs

# SSH connection information
output "ssh_commands" {
  value = [
    for conn in module.app_servers.connection_info :
    "ssh -i ~/.ssh/${conn.connection.key_name}.pem ${conn.connection.user}@${conn.host}"
  ]
}

# IPs for load balancer
output "instance_ips" {
  value = module.app_servers.network_info.instances_network.private_ips
}

# Complete information for Ansible
output "ansible_inventory" {
  value = module.app_servers.connection_info
}

Examples

Web Application (3-Tier)

# Frontend
module "frontend" {
  source = "./terraform-aws-ec2"
  
  environment  = "prod"
  company_name = "acme"
  project_name = "frontend"
  
  network_config = {
    subnet_ids         = var.public_subnet_ids
    security_group_ids = [aws_security_group.web.id]
    eip_count          = 2
  }
}

# Backend
module "backend" {
  source = "./terraform-aws-ec2"
  
  environment  = "prod"
  company_name = "acme"
  project_name = "backend"
  
  instance_config = {
    instance_type = "c6i.large"
  }
  
  network_config = {
    subnet_ids         = var.private_subnet_ids
    security_group_ids = [aws_security_group.app.id]
  }
}

Development with data volumes

module "dev_environment" {
  source = "./terraform-aws-ec2"
  
  environment  = "dev"
  company_name = "acme"
  project_name = "development"
  
  ebs_volumes = {
    docker = {
      device_name = "/dev/sdf"
      volume_size = 50
      tags = {
        Purpose = "Docker Storage"
      }
    }
  }
  
  key_pair_config = {
    create_new = true
    public_key = file("~/.ssh/dev-key.pub")
  }
}

Naming Pattern

All resources follow the pattern: {environment}-{company_name}-{project_name}

Examples:

  • Instance: prod-acme-webapp-1
  • EBS Volume: prod-acme-webapp-1-data
  • Key Pair: prod-acme-webapp-key
  • EIP: prod-acme-webapp-1-eip

Validations

The module includes automatic validations for:

  • Valid EC2 instance types
  • Volume configurations by type (IOPS, throughput)
  • Device name formats
  • Volume size ranges
  • Key pair configuration
  • IMDSv2 security requirements
  • Tags with valid characters

Quick Reference Files

main.tf

module "web_servers" {
  source = "git::https://github.com/phuru7/terraform-aws-ec2.git"
  
  environment    = var.environment
  company_name   = var.company_name
  project_name   = var.project_name
  
  network_config = {
    subnet_ids         = var.subnet_ids
    security_group_ids = var.security_group_ids
    eip_count          = var.eip_count
  }
  
  key_pair_config = {
    create_new        = false
    existing_key_name = var.key_name
  }
  
  # IMDSv2 enabled by default
  metadata_options = {
    http_tokens = "required"
  }
  
  tags = var.common_tags
}

variables.tf

variable "environment" {
  description = "Environment name"
  type        = string
}

variable "company_name" {
  description = "Company name"
  type        = string
}

variable "project_name" {
  description = "Project name"
  type        = string
}

variable "subnet_ids" {
  description = "List of subnet IDs"
  type        = list(string)
}

variable "security_group_ids" {
  description = "List of security group IDs"
  type        = list(string)
}

variable "key_name" {
  description = "EC2 Key Pair name"
  type        = string
}

variable "eip_count" {
  description = "Number of Elastic IPs"
  type        = number
  default     = 0
}

variable "common_tags" {
  description = "Common tags"
  type        = map(string)
  default     = {}
}

outputs.tf

output "instance_ids" {
  description = "EC2 instance IDs"
  value       = [for instance in module.web_servers.instances_info.instances : instance.id]
}

output "private_ips" {
  description = "Private IP addresses"
  value       = module.web_servers.network_info.instances_network.private_ips
}

output "public_ips" {
  description = "Public IP addresses"
  value       = module.web_servers.network_info.instances_network.public_ips
}

output "ssh_connection" {
  description = "SSH connection strings"
  value = [
    for conn in module.web_servers.connection_info :
    "ssh -i ~/.ssh/${conn.connection.key_name}.pem ${conn.connection.user}@${conn.host}"
  ]
}

output "instance_names" {
  description = "Instance names"
  value = [for instance in module.web_servers.instances_info.instances : instance.name]
}

terraform.tfvars

# Basic configuration
environment    = "dev"
company_name   = "acme"
project_name   = "webapp"

# Network
subnet_ids         = ["subnet-12345678", "subnet-87654321"]
security_group_ids = ["sg-abcdef123"]

# Key pair
key_name = "my-ec2-key"

# Elastic IPs (optional)
eip_count = 1

# Tags
common_tags = {
  Owner       = "DevOps Team"
  Environment = "development"
  Project     = "Web Application"
  CostCenter  = "Engineering"
  Terraform   = "true"
}

Deployment Commands

# Initialize
terraform init

# Plan
terraform plan -var-file="terraform.tfvars"

# Apply
terraform apply -var-file="terraform.tfvars"

# Destroy
terraform destroy -var-file="terraform.tfvars"

Best Practices

  1. Keep IMDSv2 enabled (default behavior)
  2. Use tfvars files per environment
  3. Apply consistent tags for billing
  4. Enable monitoring in staging and prod
  5. Use encrypted volumes (default)
  6. Configure termination protection in prod
  7. Distribute instances across multiple AZs
  8. Regular security audits

Security Notes

IMDSv2 Protection:

  • Prevents SSRF attacks on instance metadata
  • Required token-based authentication
  • Limited hop count for container environments
  • Enabled by default in this module

Encryption:

  • All EBS volumes encrypted by default
  • Root volumes encrypted by default
  • Uses AWS-managed keys unless specified

Limitations

  • Maximum 100 instances per deployment
  • EBS volumes up to 16TB
  • EIPs limited by AWS quota
  • Key pairs must exist previously if not created
  • IMDSv2 required (cannot be disabled for security)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages