Terraform AWS Bastion Host Module
Overview
This Terraform module creates an EC2 bastion host (jump server) for secure SSH access to private resources in your AWS VPC. The bastion host can be enabled or disabled as needed and includes features like Elastic IP, custom security groups, and configurable instance specifications.
Features
- Conditional creation - Enable/disable bastion host via variable
- Elastic IP assignment for consistent public IP
- Dedicated network interface with security group
- Automatic AWS optimized AMI selection
- Custom user data support
- EBS optimization option
- Configurable CIDR-based access control
- IMDSv2 enforcement for enhanced security
- Flexible tagging support
Resources Created
- EC2 Instance (Amazon Linux optimized AMI)
- Elastic IP (EIP)
- Network Interface
- Security Group with SSH ingress rules
- IAM instance profile (optional, via user configuration)
Usage
Basic Example
module "bastion" {
source = "git@github.com:webuildyourcloud/terraform-aws-bastion.git?ref=tags/0.0.7"
# Enable bastion creation
enable_bastion = true
# Naming
environment = "prod"
project = "myapp"
# Network Configuration
aws_region = "us-east-1"
vpc_id = "vpc-12345678"
subnet_id = "subnet-12345678"
# Instance Configuration
instance_type = "t3.micro"
key_name = "my-ssh-key"
# Security
admin_cidr = "203.0.113.0/24"
# Tags
tags = {
ManagedBy = "terraform"
Environment = "production"
}
}
Advanced Example with Custom AMI
module "bastion" {
source = "git@github.com:webuildyourcloud/terraform-aws-bastion.git?ref=tags/0.0.7"
enable_bastion = true
environment = "dev"
project = "research"
aws_region = "eu-west-1"
vpc_id = module.vpc.vpc_id
subnet_id = module.vpc.public_subnets[0]
# Custom instance configuration
instance_type = "t3.small"
ebs_optimized = true
key_name = "dev-bastion-key"
# Custom AMI per region
amazon_optimized_amis = {
"eu-west-1" = "ami-0123456789abcdef0"
}
# Restrict SSH access
admin_cidr = "10.0.0.0/8"
# Custom user data
user_data = file("${path.module}/bastion-userdata.sh")
tags = {
Backup = "true"
CostCenter = "engineering"
}
}
Variables
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| aws_region | The AWS region | string |
n/a | yes |
| project | Name of the project | string |
n/a | yes |
| environment | Logical name of the environment | string |
n/a | yes |
| key_name | SSH key name for the environment | string |
n/a | yes |
| vpc_id | The VPC ID to launch the instance in | string |
n/a | yes |
| subnet_id | Subnet in which the bastion needs to be deployed | string |
n/a | yes |
| enable_bastion | Enable bastion host creation | bool |
false |
no |
| instance_type | EC2 instance type | string |
"t2.micro" |
no |
| ebs_optimized | Enable EBS optimization | bool |
true |
no |
| admin_cidr | CIDR pattern to access the bastion host | string |
"0.0.0.0/0" |
no |
| amazon_optimized_amis | Map from region to AMI (uses latest Amazon Linux if not specified) | map(string) |
{} |
no |
| user_data | User data for bastion EC2 instance | string |
"" |
no |
| tags | Map of tags to apply on the resources | map(string) |
{} |
no |
Outputs
| Name | Description |
|---|---|
| instance_id | ID of the created bastion instance |
| public_ip | Public IP address of the bastion instance |
Requirements
| Name | Version |
|---|---|
| terraform | >= 0.13 |
| aws | Latest |
| template | Latest |
Security Features
IMDSv2 Enforcement
The bastion host is configured with Instance Metadata Service Version 2 (IMDSv2) enforcement:
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
}
This provides enhanced security against SSRF attacks and unauthorized metadata access.
Security Group Configuration
The module creates a dedicated security group with:
- SSH (port 22) ingress from specified CIDR blocks (configurable via
admin_cidr) - All egress traffic allowed (for package updates and outbound connections)
Network Configuration
- Dedicated network interface for better security isolation
- Elastic IP for consistent public addressing
- Deployed in public subnet for external access
Important Notes
- Enable Flag: By default,
enable_bastionis set tofalse. You must explicitly set it totrueto create the bastion host - SSH Key: Ensure the SSH key specified in
key_nameexists in the target region before applying - Cost Consideration: The bastion host and Elastic IP incur hourly charges even when idle
- AMI Selection: If
amazon_optimized_amisis not specified, the module automatically selects the latest Amazon Linux AMI - Access Control: Restrict
admin_cidrto specific IP ranges for production use (avoid0.0.0.0/0) - User Data: Default user data template is included at
template/user_data.sh
Example Use Cases
Development Environment
module "dev_bastion" {
source = "git@github.com:webuildyourcloud/terraform-aws-bastion.git"
enable_bastion = true
environment = "dev"
project = "myapp"
instance_type = "t3.micro"
admin_cidr = "0.0.0.0/0" # Open for development
}
Production Environment
module "prod_bastion" {
source = "git@github.com:webuildyourcloud/terraform-aws-bastion.git"
enable_bastion = true
environment = "prod"
project = "myapp"
instance_type = "t3.small"
ebs_optimized = true
admin_cidr = "203.0.113.0/24" # Restricted to office network
}
Accessing the Bastion Host
After creation, connect to the bastion using:
ssh -i /path/to/key.pem ec2-user@<bastion-public-ip>
To tunnel through the bastion to private resources:
ssh -i /path/to/key.pem -A -L 8080:private-server:80 ec2-user@<bastion-public-ip>
Best Practices
- Use restrictive CIDR blocks for
admin_cidrin production - Enable EBS optimization for better performance
- Use Session Manager as an alternative to SSH when possible
- Regularly update the bastion AMI for security patches
- Implement CloudWatch logging for SSH sessions
- Consider using AWS Systems Manager Session Manager instead of traditional SSH
- Tag resources appropriately for cost allocation
- Disable bastion when not in use to reduce costs
Troubleshooting
Cannot connect to bastion
- Verify the security group allows your IP in
admin_cidr - Check the Elastic IP is properly associated
- Ensure the SSH key matches the one specified in
key_name - Verify the subnet has an Internet Gateway attached
Instance not created
- Confirm
enable_bastionis set totrue - Check AWS service quotas for EC2 instances
- Verify subnet and VPC exist
License
This module is provided as-is for use within your organization.
Description
Terraform module for provisioning AWS bastion host with security groups and elastic IP
Languages
HCL
85%
Shell
15%