AWS EC2 Bastion Host in Public Subnet
Step-00: Introduction¶
- For VPC switch Availability Zones from Static to Dynamic using Datasource
aws_availability_zones - Create EC2 Key pair that will be used for connecting to Bastion Host and EKS Node Group EC2 VM Instances
- EC2 Bastion Host - Terraform Input Variables
- EC2 Bastion Host - AWS Security Group Terraform Module
- EC2 Bastion Host - AWS AMI Datasource (Dynamically lookup the latest Amazon2 Linux AMI)
- EC2 Bastion Host - AWS EC2 Instance Terraform Module
- EC2 Bastion Host - Terraform Resource AWS EC2 Elastic IP
- EC2 Bastion Host - Terraform Provisioners
- File provisioner
- remote-exec provisioner
- local-exec provisioner
- EC2 Bastion Host - Output Values
- EC2 Bastion Host - ec2bastion.auto.tfvars
- EKS Input Variables
- EKS Local Values
- EKS Tags in VPC for Public and Private Subnets
- Execute Terraform Commands and Test
- Elastic IP - depends_on Meta Argument
Step-01: For VPC switch Availability Zones from Static to Dynamic¶
- Datasource: aws_availability_zones
- File Name:
c3-02-vpc-module.tffor changes 1 and 2# Change-1: Add Datasource named aws_availability_zones # AWS Availability Zones Datasource data "aws_availability_zones" "available" { } # Change-2: Update the same in VPC Module azs = data.aws_availability_zones.available.names # Change-3: Comment vpc_availability_zones variable in File: c3-01-vpc-variables.tf /* variable "vpc_availability_zones" { description = "VPC Availability Zones" type = list(string) default = ["us-east-1a", "us-east-1b"] } */ # Change-4: Comment hard-coded Availability Zones variable in File: vpc.auto.tfvars #vpc_availability_zones = ["us-east-1a", "us-east-1b"]
Step-02: Create EC2 Key pair and save it¶
- Go to Services -> EC2 -> Network & Security -> Key Pairs -> Create Key Pair
- Name: eks-terraform-key
- Key Pair Type: RSA (leave to defaults)
- Private key file format: .pem
- Click on Create key pair
- COPY the downloaded key pair to
terraform-manifests/private-keyfolder - Provide permissions as
chmod 400 keypair-name
Step-03: c4-01-ec2bastion-variables.tf¶
# AWS EC2 Instance Terraform Variables
# AWS EC2 Instance Type
variable "instance_type" {
description = "EC2 Instance Type"
type = string
default = "t3.micro"
}
# AWS EC2 Instance Key Pair
variable "instance_keypair" {
description = "AWS EC2 Key pair that need to be associated with EC2 Instance"
type = string
default = "eks-terraform-key"
}
Step-04: c4-03-ec2bastion-securitygroups.tf¶
# AWS EC2 Security Group Terraform Module
# Security Group for Public Bastion Host
module "public_bastion_sg" {
source = "terraform-aws-modules/security-group/aws"
version = "4.5.0"
name = "${local.name}-public-bastion-sg"
description = "Security Group with SSH port open for everybody (IPv4 CIDR), egress ports are all world open"
vpc_id = module.vpc.vpc_id
# Ingress Rules & CIDR Blocks
ingress_rules = ["ssh-tcp"]
ingress_cidr_blocks = ["0.0.0.0/0"]
# Egress Rule - all-all open
egress_rules = ["all-all"]
tags = local.common_tags
}
Step-05: c4-04-ami-datasource.tf¶
# Get latest AMI ID for Amazon Linux2 OS
data "aws_ami" "amzlinux2" {
most_recent = true
owners = [ "amazon" ]
filter {
name = "name"
values = [ "amzn2-ami-hvm-*-gp2" ]
}
filter {
name = "root-device-type"
values = [ "ebs" ]
}
filter {
name = "virtualization-type"
values = [ "hvm" ]
}
filter {
name = "architecture"
values = [ "x86_64" ]
}
}
Step-06: c4-05-ec2bastion-instance.tf¶
# AWS EC2 Instance Terraform Module
# Bastion Host - EC2 Instance that will be created in VPC Public Subnet
module "ec2_public" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "3.3.0"
# insert the required variables here
name = "${local.name}-BastionHost"
ami = data.aws_ami.amzlinux2.id
instance_type = var.instance_type
key_name = var.instance_keypair
#monitoring = true
subnet_id = module.vpc.public_subnets[0]
vpc_security_group_ids = [module.public_bastion_sg.security_group_id]
tags = local.common_tags
}
Step-07: c4-06-ec2bastion-elasticip.tf¶
# Create Elastic IP for Bastion Host
# Resource - depends_on Meta-Argument
resource "aws_eip" "bastion_eip" {
depends_on = [ module.ec2_public, module.vpc ]
instance = module.ec2_public.id
vpc = true
tags = local.common_tags
}
Step-08: c4-07-ec2bastion-provisioners.tf¶
# Create a Null Resource and Provisioners
resource "null_resource" "copy_ec2_keys" {
depends_on = [module.ec2_public]
# Connection Block for Provisioners to connect to EC2 Instance
connection {
type = "ssh"
host = aws_eip.bastion_eip.public_ip
user = "ec2-user"
password = ""
private_key = file("private-key/eks-terraform-key.pem")
}
## File Provisioner: Copies the terraform-key.pem file to /tmp/terraform-key.pem
provisioner "file" {
source = "private-key/eks-terraform-key.pem"
destination = "/tmp/eks-terraform-key.pem"
}
## Remote Exec Provisioner: Using remote-exec provisioner fix the private key permissions on Bastion Host
provisioner "remote-exec" {
inline = [
"sudo chmod 400 /tmp/eks-terraform-key.pem"
]
}
## Local Exec Provisioner: local-exec provisioner (Creation-Time Provisioner - Triggered during Create Resource)
provisioner "local-exec" {
command = "echo VPC created on `date` and VPC ID: ${module.vpc.vpc_id} >> creation-time-vpc-id.txt"
working_dir = "local-exec-output-files/"
#on_failure = continue
}
}
Step-09: ec2bastion.auto.tfvars¶
Step-10: c4-02-ec2bastion-outputs.tf¶
# AWS EC2 Instance Terraform Outputs
# Public EC2 Instances - Bastion Host
## ec2_bastion_public_instance_ids
output "ec2_bastion_public_instance_ids" {
description = "List of IDs of instances"
value = module.ec2_public.id
}
## ec2_bastion_public_ip
output "ec2_bastion_eip" {
description = "Elastic IP associated to the Bastion Host"
value = aws_eip.bastion_eip.public_ip
}
Step-11: c5-01-eks-variables.tf¶
# EKS Cluster Input Variables
variable "cluster_name" {
description = "Name of the EKS cluster. Also used as a prefix in names of related resources."
type = string
default = "eksdemo"
}
Step-12: eks.auto.tfvars¶
Step-13: c2-02-local-values.tf¶
Step-14: c3-02-vpc-module.tf¶
- Update VPC Tags to Support EKS
# Create VPC Terraform Module module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "3.11.0" #version = "~> 3.11" # VPC Basic Details name = local.eks_cluster_name cidr = var.vpc_cidr_block azs = data.aws_availability_zones.available.names public_subnets = var.vpc_public_subnets private_subnets = var.vpc_private_subnets # Database Subnets database_subnets = var.vpc_database_subnets create_database_subnet_group = var.vpc_create_database_subnet_group create_database_subnet_route_table = var.vpc_create_database_subnet_route_table # create_database_internet_gateway_route = true # create_database_nat_gateway_route = true # NAT Gateways - Outbound Communication enable_nat_gateway = var.vpc_enable_nat_gateway single_nat_gateway = var.vpc_single_nat_gateway # VPC DNS Parameters enable_dns_hostnames = true enable_dns_support = true tags = local.common_tags vpc_tags = local.common_tags # Additional Tags to Subnets public_subnet_tags = { Type = "Public Subnets" "kubernetes.io/role/elb" = 1 "kubernetes.io/cluster/${local.eks_cluster_name}" = "shared" } private_subnet_tags = { Type = "private-subnets" "kubernetes.io/role/internal-elb" = 1 "kubernetes.io/cluster/${local.eks_cluster_name}" = "shared" } database_subnet_tags = { Type = "database-subnets" } }
Step-15: Execute Terraform Commands¶
# Terraform Initialize
terraform init
# Terraform Validate
terraform validate
# Terraform plan
terraform plan
# Terraform Apply
terraform apply -auto-approve
Step-16: Verify the following¶
- Verify VPC Tags
- Verify Bastion EC2 Instance
- Verify Bastion EC2 Instance Security Group
- Connect to Bastion EC2 Instnace
# Connect to Bastion EC2 Instance ssh -i private-key/eks-terraform-key.pem ec2-user@<Elastic-IP-Bastion-Host> sudo su - # Verify File and Remote Exec Provisioners moved the EKS PEM file cd /tmp ls -lrta Observation: We should find the file named "eks-terraform-key.pem" moved from our local desktop to Bastion EC2 Instance "/tmp" folder
Step-17: Clean-Up¶
# Delete Resources
terraform destroy -auto-approve
terraform apply -destroy -auto-approve
# Delete Files
rm -rf .terraform* terraform.tfstate*
🎉 New Course
Ultimate DevOps Real-World Project Implementation on AWS
$15.99
$84.99
81% OFF
DEVOPS2026FEB
Enroll Now on Udemy →
🎉 Offer