Before deploying our super cool infrastructure, setting up a landing zone is essential. A landing zone is a foundational infrastructure setup that includes networking, security, and essential services required for a secure and scalable environment. In this article, I'll guide you through setting up a landing zone for your EKS cluster using Terraform.
Clone the repo here.
Prerequisites
Ensure you have the following tools installed and configured:
- Terraform installed.
- AWS CLI installed and configured with necessary permissions (BlueSentry cross-account role).
Step 1: Define Variables
First, create a terraform.tfvars file with the necessary variables:
company = "" domain = "" openvpn_instance_type = "" region = ""
Step 2: Create VPC Configuration
Create a vpc.tf file to define the Virtual Private Cloud (VPC), subnets, and related networking components:
data "aws_availability_zones" "main" {} locals { availability_zones = [ data.aws_availability_zones.main.names[0], data.aws_availability_zones.main.names[1], data.aws_availability_zones.main.names[2], ] public_subnets = [ cidrsubnet(var.vpc_cidr, 6, 0), cidrsubnet(var.vpc_cidr, 6, 1), cidrsubnet(var.vpc_cidr, 6, 2), ] private_subnets = [ cidrsubnet(var.vpc_cidr, 6, 4), cidrsubnet(var.vpc_cidr, 6, 5), cidrsubnet(var.vpc_cidr, 6, 6), ] database_subnets = [ cidrsubnet(var.vpc_cidr, 6, 7), cidrsubnet(var.vpc_cidr, 6, 8), cidrsubnet(var.vpc_cidr, 6, 9), ] vpc_route_tables = flatten([ module.vpc.private_route_table_ids, module.vpc.public_route_table_ids, ]) } module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 5.8.1" azs = local.availability_zones cidr = var.vpc_cidr create_database_subnet_group = true create_flow_log_cloudwatch_iam_role = true create_flow_log_cloudwatch_log_group = true database_subnets = local.database_subnets enable_dhcp_options = true enable_dns_hostnames = true enable_dns_support = true enable_flow_log = true enable_nat_gateway = true flow_log_cloudwatch_log_group_retention_in_days = 7 flow_log_max_aggregation_interval = 60 name = var.environment one_nat_gateway_per_az = var.vpc_redundancy ? true : false private_subnet_suffix = "private" private_subnets = local.private_subnets public_subnets = local.public_subnets public_subnet_tags = { "kubernetes.io/cluster/${module.eks.cluster_name}" = "shared" "kubernetes.io/role/elb" = 1 } single_nat_gateway = var.vpc_redundancy ? false : true tags = local.tags } module "vpc_endpoints" { source = "terraform-aws-modules/vpc/aws//modules/vpc-endpoints" version = "~> 5.8.1" vpc_id = module.vpc.vpc_id tags = local.tags endpoints = { s3 = { route_table_ids = local.vpc_route_tables service = "s3" service_type = "Gateway" tags = { Name = "s3-vpc-endpoint" } } } }
Step 3: OpenVPN
Create a openvpn.tf file for security groups and roles and EC2:
data "aws_ami" "openvpnas" { most_recent = true filter { name = "name" values = [ "OpenVPN Access Server Community Image-fe8020db-5343-4c43-9e65-5ed4a825c931*" ] } owners = [ "679593333241", ] include_deprecated = true } data "aws_iam_policy_document" "ec2_role" { statement { effect = "Allow" principals { identifiers = ["ec2.amazonaws.com"] type = "Service" } actions = ["sts:AssumeRole"] } } data "aws_iam_policy" "AmazonSSMManagedInstanceCore" { name = "AmazonSSMManagedInstanceCore" } data "aws_route53_zone" "this" { name = var.domain } locals { openvpn_name = "openvpn" } resource "aws_eip" "vpn" { tags = merge(var.tags, { Name = "openvpn" }) lifecycle { create_before_destroy = true } } resource "aws_eip_association" "eip_vpn" { instance_id = aws_instance.openvpn.id allocation_id = aws_eip.vpn.id } resource "aws_iam_instance_profile" "this" { name = "${var.company}_ec2_role" role = aws_iam_role.ec2_role.name tags = var.tags } resource "aws_iam_role" "ec2_role" { name = "${var.company}_ec2_role" assume_role_policy = data.aws_iam_policy_document.ec2_role.json tags = var.tags } resource "aws_iam_role_policy_attachment" "AmazonSSMManagedInstanceCore" { role = aws_iam_role.ec2_role.name policy_arn = data.aws_iam_policy.AmazonSSMManagedInstanceCore.arn } resource "aws_instance" "openvpn" { ami = data.aws_ami.openvpnas.id disable_api_termination = true ebs_optimized = true iam_instance_profile = aws_iam_instance_profile.this.name instance_type = var.openvpn_instance_type key_name = module.dev.ssh_keypair monitoring = true subnet_id = module.dev.public_subnets[0] vpc_security_group_ids = [aws_security_group.openvpn.id] metadata_options { http_endpoint = "enabled" http_put_response_hop_limit = 3 http_tokens = "required" } tags = merge(var.tags, { "Name" = local.openvpn_name "Patch Group" = "A" "backup" = "true" }) volume_tags = merge(var.tags, { "Name" = "${local.openvpn_name}_vol" "backup" = "true" }) root_block_device { encrypted = true volume_type = "gp3" } lifecycle { ignore_changes = [user_data, ami] } } resource "aws_route53_record" "vpn" { zone_id = data.aws_route53_zone.this.zone_id name = "vpn" type = "A" ttl = "300" records = [aws_eip.vpn.public_ip] } resource "aws_security_group" "openvpn" { name = local.openvpn_name vpc_id = module.dev.vpc_id description = "OpenVPN security group" tags = merge(var.tags, { Name = "${local.openvpn_name}-sg" }) } resource "aws_security_group_rule" "egress_all" { type = "egress" protocol = -1 from_port = 0 to_port = 0 cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.openvpn.id } resource "aws_security_group_rule" "ingress_tcp443" { type = "ingress" protocol = "tcp" from_port = 443 to_port = 443 cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.openvpn.id } resource "aws_security_group_rule" "ingress_tcp80" { type = "ingress" protocol = "tcp" from_port = 80 to_port = 80 cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.openvpn.id } resource "aws_security_group_rule" "ingress_udp1194" { type = "ingress" protocol = "udp" from_port = 1194 to_port = 1194 cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.openvpn.id }
Step 4: S3 Buckets for State Management
Create a backend file to define S3 buckets for storing Terraform state files:
terraform { backend "s3" { bucket = <BUCKET_NAME> key = "infra/terraform.tfstate" region = "us-east-1" } }
Step 5: Initialize and Apply Terraform
Initialize Terraform and apply the configuration:
terraform init terraform validate terraform plan -out=plan.out terraform apply plan.out
You've successfully set up a landing zone for your EKS cluster. This foundational setup includes networking components, security configurations, and essential services, providing a secure and scalable environment for deploying your EKS cluster.
Visit my website here.