270 lines
7.4 KiB
HCL
270 lines
7.4 KiB
HCL
terraform {
|
|
required_version = ">= 1.7"
|
|
required_providers {
|
|
aws = {
|
|
source = "hashicorp/aws"
|
|
version = "~> 5.0"
|
|
}
|
|
kubernetes = {
|
|
source = "hashicorp/kubernetes"
|
|
version = "~> 2.27"
|
|
}
|
|
}
|
|
backend "s3" {
|
|
bucket = "veylant-terraform-state"
|
|
key = "production/eks/terraform.tfstate"
|
|
region = "eu-west-3"
|
|
encrypt = true
|
|
dynamodb_table = "veylant-terraform-locks"
|
|
}
|
|
}
|
|
|
|
provider "aws" {
|
|
region = var.aws_region
|
|
default_tags {
|
|
tags = {
|
|
Project = "veylant-ia"
|
|
Environment = "production"
|
|
ManagedBy = "terraform"
|
|
}
|
|
}
|
|
}
|
|
|
|
# ──────────────────────────────────────────────
|
|
# VPC — 3 public + 3 private subnets across AZs
|
|
# ──────────────────────────────────────────────
|
|
module "vpc" {
|
|
source = "terraform-aws-modules/vpc/aws"
|
|
version = "~> 5.5"
|
|
|
|
name = "veylant-production"
|
|
cidr = var.vpc_cidr
|
|
|
|
azs = ["${var.aws_region}a", "${var.aws_region}b", "${var.aws_region}c"]
|
|
private_subnets = var.private_subnet_cidrs
|
|
public_subnets = var.public_subnet_cidrs
|
|
|
|
enable_nat_gateway = true
|
|
single_nat_gateway = false # 1 NAT GW per AZ for HA
|
|
enable_dns_hostnames = true
|
|
enable_dns_support = true
|
|
|
|
# Required tags for EKS auto-discovery of subnets.
|
|
private_subnet_tags = {
|
|
"kubernetes.io/role/internal-elb" = "1"
|
|
"kubernetes.io/cluster/${var.cluster_name}" = "owned"
|
|
}
|
|
public_subnet_tags = {
|
|
"kubernetes.io/role/elb" = "1"
|
|
"kubernetes.io/cluster/${var.cluster_name}" = "owned"
|
|
}
|
|
}
|
|
|
|
# ──────────────────────────────────────────────
|
|
# EKS Cluster — Kubernetes 1.31, eu-west-3
|
|
# ──────────────────────────────────────────────
|
|
module "eks" {
|
|
source = "terraform-aws-modules/eks/aws"
|
|
version = "~> 20.0"
|
|
|
|
cluster_name = var.cluster_name
|
|
cluster_version = "1.31"
|
|
|
|
vpc_id = module.vpc.vpc_id
|
|
subnet_ids = module.vpc.private_subnet_ids
|
|
cluster_endpoint_public_access = true # Access via kubectl from CI/CD
|
|
|
|
# Enable IRSA — required for pod-level IAM roles (backup, Vault).
|
|
enable_irsa = true
|
|
|
|
cluster_addons = {
|
|
aws-ebs-csi-driver = {
|
|
most_recent = true
|
|
service_account_role_arn = module.irsa_ebs_csi.iam_role_arn
|
|
}
|
|
coredns = {
|
|
most_recent = true
|
|
}
|
|
kube-proxy = {
|
|
most_recent = true
|
|
}
|
|
vpc-cni = {
|
|
most_recent = true
|
|
before_compute = true
|
|
}
|
|
}
|
|
|
|
eks_managed_node_groups = {
|
|
# One node group per AZ for topology-aware scheduling.
|
|
veylant-az-a = {
|
|
name = "veylant-az-a"
|
|
subnet_ids = [module.vpc.private_subnets[0]]
|
|
instance_types = [var.node_instance_type]
|
|
min_size = 1
|
|
max_size = 5
|
|
desired_size = 2
|
|
ami_type = "AL2_x86_64"
|
|
disk_size = 50
|
|
|
|
labels = {
|
|
"topology.kubernetes.io/zone" = "${var.aws_region}a"
|
|
workload = "veylant"
|
|
}
|
|
}
|
|
|
|
veylant-az-b = {
|
|
name = "veylant-az-b"
|
|
subnet_ids = [module.vpc.private_subnets[1]]
|
|
instance_types = [var.node_instance_type]
|
|
min_size = 1
|
|
max_size = 5
|
|
desired_size = 2
|
|
ami_type = "AL2_x86_64"
|
|
disk_size = 50
|
|
|
|
labels = {
|
|
"topology.kubernetes.io/zone" = "${var.aws_region}b"
|
|
workload = "veylant"
|
|
}
|
|
}
|
|
|
|
veylant-az-c = {
|
|
name = "veylant-az-c"
|
|
subnet_ids = [module.vpc.private_subnets[2]]
|
|
instance_types = [var.node_instance_type]
|
|
min_size = 1
|
|
max_size = 5
|
|
desired_size = 2
|
|
ami_type = "AL2_x86_64"
|
|
disk_size = 50
|
|
|
|
labels = {
|
|
"topology.kubernetes.io/zone" = "${var.aws_region}c"
|
|
workload = "veylant"
|
|
}
|
|
}
|
|
}
|
|
|
|
tags = {
|
|
Environment = "production"
|
|
Cluster = var.cluster_name
|
|
}
|
|
}
|
|
|
|
# ──────────────────────────────────────────────
|
|
# IRSA — IAM Roles for Service Accounts
|
|
# ──────────────────────────────────────────────
|
|
|
|
# EBS CSI Driver IRSA
|
|
module "irsa_ebs_csi" {
|
|
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
|
|
version = "~> 5.39"
|
|
|
|
role_name = "veylant-ebs-csi-driver"
|
|
attach_ebs_csi_policy = true
|
|
|
|
oidc_providers = {
|
|
main = {
|
|
provider_arn = module.eks.oidc_provider_arn
|
|
namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"]
|
|
}
|
|
}
|
|
}
|
|
|
|
# Backup role IRSA (S3 write for pg_dump)
|
|
module "irsa_backup" {
|
|
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
|
|
version = "~> 5.39"
|
|
|
|
role_name = "veylant-backup-role"
|
|
|
|
role_policy_arns = {
|
|
backup = aws_iam_policy.backup_s3.arn
|
|
}
|
|
|
|
oidc_providers = {
|
|
main = {
|
|
provider_arn = module.eks.oidc_provider_arn
|
|
namespace_service_accounts = ["veylant:veylant-backup"]
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "aws_iam_policy" "backup_s3" {
|
|
name = "veylant-backup-s3"
|
|
description = "Allow Veylant backup job to write to S3 backup bucket"
|
|
|
|
policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [
|
|
{
|
|
Effect = "Allow"
|
|
Action = [
|
|
"s3:PutObject",
|
|
"s3:GetObject",
|
|
"s3:ListBucket",
|
|
"s3:DeleteObject"
|
|
]
|
|
Resource = [
|
|
"arn:aws:s3:::veylant-backups-production",
|
|
"arn:aws:s3:::veylant-backups-production/*"
|
|
]
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
# ──────────────────────────────────────────────
|
|
# S3 Backup Bucket with 7-day lifecycle
|
|
# ──────────────────────────────────────────────
|
|
resource "aws_s3_bucket" "backups" {
|
|
bucket = "veylant-backups-production"
|
|
}
|
|
|
|
resource "aws_s3_bucket_versioning" "backups" {
|
|
bucket = aws_s3_bucket.backups.id
|
|
versioning_configuration {
|
|
status = "Enabled"
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_server_side_encryption_configuration" "backups" {
|
|
bucket = aws_s3_bucket.backups.id
|
|
rule {
|
|
apply_server_side_encryption_by_default {
|
|
sse_algorithm = "AES256"
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_lifecycle_configuration" "backups" {
|
|
bucket = aws_s3_bucket.backups.id
|
|
|
|
rule {
|
|
id = "expire-old-backups"
|
|
status = "Enabled"
|
|
|
|
filter {
|
|
prefix = "postgres/"
|
|
}
|
|
|
|
# Delete backups older than 7 days.
|
|
expiration {
|
|
days = 7
|
|
}
|
|
|
|
# Clean up incomplete multipart uploads.
|
|
abort_incomplete_multipart_upload {
|
|
days_after_initiation = 1
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_public_access_block" "backups" {
|
|
bucket = aws_s3_bucket.backups.id
|
|
block_public_acls = true
|
|
block_public_policy = true
|
|
ignore_public_acls = true
|
|
restrict_public_buckets = true
|
|
}
|