Skip to content

Instantly share code, notes, and snippets.

@19h
Created March 19, 2025 14:45
Show Gist options
  • Save 19h/285a771116ee464a4092e3facc3159b7 to your computer and use it in GitHub Desktop.
Save 19h/285a771116ee464a4092e3facc3159b7 to your computer and use it in GitHub Desktop.
Kubernetes setup in terraform - Russian Elite Engineer vs American Dipshit Engineer
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.16"
}
helm = {
source = "hashicorp/helm"
version = "~> 2.8"
}
random = {
source = "hashicorp/random"
version = "~> 3.4"
}
tls = {
source = "hashicorp/tls"
version = "~> 4.0"
}
}
required_version = ">= 1.2.0"
backend "s3" {
bucket = "terraform-state-k8s-production"
key = "terraform.tfstate"
region = "eu-central-1"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
provider "aws" {
region = var.region
default_tags {
tags = {
Environment = var.environment
Terraform = "true"
Project = "k8s-cluster"
}
}
}
# Variables
variable "region" {
description = "AWS region"
type = string
default = "eu-central-1"
}
variable "environment" {
description = "Environment name"
type = string
default = "production"
}
variable "cluster_name" {
description = "EKS cluster name"
type = string
default = "production-eks"
}
variable "vpc_cidr" {
description = "VPC CIDR block"
type = string
default = "10.0.0.0/16"
}
variable "availability_zones" {
description = "List of availability zones"
type = list(string)
default = ["eu-central-1a", "eu-central-1b", "eu-central-1c"]
}
variable "instance_types" {
description = "EC2 instance types for worker nodes"
type = list(string)
default = ["t3.large", "t3.xlarge", "m5.large"]
}
variable "node_group_min_size" {
description = "Minimum size of node group"
type = number
default = 3
}
variable "node_group_max_size" {
description = "Maximum size of node group"
type = number
default = 10
}
variable "node_group_desired_size" {
description = "Desired size of node group"
type = number
default = 3
}
resource "random_id" "suffix" {
byte_length = 4
}
# Network Infrastructure
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.environment}-vpc-${random_id.suffix.hex}"
}
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.environment}-public-${var.availability_zones[count.index]}"
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"
}
}
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + length(var.availability_zones))
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.environment}-private-${var.availability_zones[count.index]}"
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/internal-elb" = "1"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.environment}-igw"
}
}
resource "aws_eip" "nat" {
count = length(var.availability_zones)
vpc = true
tags = {
Name = "${var.environment}-nat-eip-${count.index}"
}
}
resource "aws_nat_gateway" "main" {
count = length(var.availability_zones)
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.environment}-nat-${count.index}"
}
depends_on = [aws_internet_gateway.main]
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.environment}-public-rt"
}
}
resource "aws_route_table" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = {
Name = "${var.environment}-private-rt-${count.index}"
}
}
resource "aws_route_table_association" "public" {
count = length(var.availability_zones)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = length(var.availability_zones)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
# Security Groups
resource "aws_security_group" "eks_cluster" {
name = "${var.environment}-eks-cluster-sg"
description = "Security group for EKS control plane"
vpc_id = aws_vpc.main.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.environment}-eks-cluster-sg"
}
}
resource "aws_security_group_rule" "eks_cluster_ingress_https" {
security_group_id = aws_security_group.eks_cluster.id
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [var.vpc_cidr]
description = "Allow HTTPS traffic from VPC"
}
resource "aws_security_group" "eks_nodes" {
name = "${var.environment}-eks-nodes-sg"
description = "Security group for EKS worker nodes"
vpc_id = aws_vpc.main.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.environment}-eks-nodes-sg"
"kubernetes.io/cluster/${var.cluster_name}" = "owned"
}
}
resource "aws_security_group_rule" "eks_nodes_ingress_self" {
security_group_id = aws_security_group.eks_nodes.id
type = "ingress"
from_port = 0
to_port = 65535
protocol = "-1"
source_security_group_id = aws_security_group.eks_nodes.id
description = "Allow all traffic between nodes"
}
resource "aws_security_group_rule" "eks_nodes_ingress_cluster" {
security_group_id = aws_security_group.eks_nodes.id
type = "ingress"
from_port = 1025
to_port = 65535
protocol = "tcp"
source_security_group_id = aws_security_group.eks_cluster.id
description = "Allow worker kubelet and pods to receive communication from the cluster control plane"
}
resource "aws_security_group_rule" "eks_cluster_ingress_nodes" {
security_group_id = aws_security_group.eks_cluster.id
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
source_security_group_id = aws_security_group.eks_nodes.id
description = "Allow pods to communicate with the cluster API Server"
}
# IAM for EKS
resource "aws_iam_role" "eks_cluster" {
name = "${var.environment}-eks-cluster-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "eks.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_role_policy_attachment" "eks_cluster_policy" {
role = aws_iam_role.eks_cluster.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
}
resource "aws_iam_role_policy_attachment" "eks_service_policy" {
role = aws_iam_role.eks_cluster.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
}
resource "aws_iam_role" "eks_nodes" {
name = "${var.environment}-eks-nodes-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_role_policy_attachment" "eks_worker_node_policy" {
role = aws_iam_role.eks_nodes.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
}
resource "aws_iam_role_policy_attachment" "eks_cni_policy" {
role = aws_iam_role.eks_nodes.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
}
resource "aws_iam_role_policy_attachment" "eks_container_registry_policy" {
role = aws_iam_role.eks_nodes.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
}
resource "aws_iam_role_policy_attachment" "eks_cloudwatch_policy" {
role = aws_iam_role.eks_nodes.name
policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
}
# Allow autoscaling for nodes
resource "aws_iam_role_policy_attachment" "eks_autoscaler_policy" {
role = aws_iam_role.eks_nodes.name
policy_arn = aws_iam_policy.cluster_autoscaler.arn
}
resource "aws_iam_policy" "cluster_autoscaler" {
name = "${var.environment}-cluster-autoscaler-policy"
description = "Policy for cluster autoscaler"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeAutoScalingInstances",
"autoscaling:DescribeLaunchConfigurations",
"autoscaling:DescribeTags",
"autoscaling:SetDesiredCapacity",
"autoscaling:TerminateInstanceInAutoScalingGroup",
"ec2:DescribeLaunchTemplateVersions"
]
Resource = "*"
Effect = "Allow"
}
]
})
}
# EKS Cluster
resource "aws_eks_cluster" "main" {
name = var.cluster_name
role_arn = aws_iam_role.eks_cluster.arn
version = "1.26"
vpc_config {
subnet_ids = concat(aws_subnet.private[*].id)
security_group_ids = [aws_security_group.eks_cluster.id]
endpoint_private_access = true
endpoint_public_access = true
}
depends_on = [
aws_iam_role_policy_attachment.eks_cluster_policy,
aws_iam_role_policy_attachment.eks_service_policy
]
encryption_config {
provider {
key_arn = aws_kms_key.eks.arn
}
resources = ["secrets"]
}
enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
tags = {
Name = var.cluster_name
}
}
resource "aws_kms_key" "eks" {
description = "EKS Secret Encryption Key"
deletion_window_in_days = 7
enable_key_rotation = true
tags = {
Name = "${var.environment}-eks-kms-key"
}
}
resource "aws_cloudwatch_log_group" "eks" {
name = "/aws/eks/${var.cluster_name}/cluster"
retention_in_days = 30
tags = {
Name = "${var.environment}-eks-cloudwatch-log-group"
}
}
# EKS Node Groups
resource "aws_eks_node_group" "main" {
count = length(var.availability_zones)
cluster_name = aws_eks_cluster.main.name
node_group_name = "${var.environment}-node-group-${count.index}"
node_role_arn = aws_iam_role.eks_nodes.arn
subnet_ids = [aws_subnet.private[count.index].id]
instance_types = var.instance_types
ami_type = "AL2_x86_64"
capacity_type = "ON_DEMAND"
scaling_config {
desired_size = var.node_group_desired_size
max_size = var.node_group_max_size
min_size = var.node_group_min_size
}
update_config {
max_unavailable = 1
}
# Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling.
# Otherwise, EKS will not be able to properly delete EC2 Instances and Elastic Network Interfaces.
depends_on = [
aws_iam_role_policy_attachment.eks_worker_node_policy,
aws_iam_role_policy_attachment.eks_cni_policy,
aws_iam_role_policy_attachment.eks_container_registry_policy,
]
lifecycle {
create_before_destroy = true
}
tags = {
"k8s.io/cluster-autoscaler/enabled" = "true"
"k8s.io/cluster-autoscaler/${var.cluster_name}" = "owned"
}
}
# Configure kubectl to use the EKS cluster
resource "null_resource" "kubectl_config" {
depends_on = [aws_eks_cluster.main]
provisioner "local-exec" {
command = "aws eks update-kubeconfig --name ${var.cluster_name} --region ${var.region}"
}
}
# Kubernetes provider configuration
provider "kubernetes" {
host = aws_eks_cluster.main.endpoint
cluster_ca_certificate = base64decode(aws_eks_cluster.main.certificate_authority[0].data)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
args = ["eks", "get-token", "--cluster-name", aws_eks_cluster.main.name]
command = "aws"
}
}
provider "helm" {
kubernetes {
host = aws_eks_cluster.main.endpoint
cluster_ca_certificate = base64decode(aws_eks_cluster.main.certificate_authority[0].data)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
args = ["eks", "get-token", "--cluster-name", aws_eks_cluster.main.name]
command = "aws"
}
}
}
# Kubernetes Add-ons
resource "kubernetes_namespace" "monitoring" {
metadata {
name = "monitoring"
}
depends_on = [aws_eks_node_group.main]
}
resource "kubernetes_namespace" "ingress" {
metadata {
name = "ingress-nginx"
}
depends_on = [aws_eks_node_group.main]
}
resource "helm_release" "metrics_server" {
name = "metrics-server"
repository = "https://kubernetes-sigs.github.io/metrics-server/"
chart = "metrics-server"
namespace = "kube-system"
version = "3.8.2"
set {
name = "apiService.create"
value = "true"
}
depends_on = [aws_eks_node_group.main]
}
resource "helm_release" "cluster_autoscaler" {
name = "cluster-autoscaler"
repository = "https://kubernetes.github.io/autoscaler"
chart = "cluster-autoscaler"
namespace = "kube-system"
version = "9.24.0"
set {
name = "autoDiscovery.clusterName"
value = aws_eks_cluster.main.name
}
set {
name = "awsRegion"
value = var.region
}
set {
name = "rbac.serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.eks_nodes.arn
}
depends_on = [aws_eks_node_group.main, helm_release.metrics_server]
}
resource "helm_release" "prometheus" {
name = "prometheus"
repository = "https://prometheus-community.github.io/helm-charts"
chart = "kube-prometheus-stack"
namespace = kubernetes_namespace.monitoring.metadata[0].name
version = "45.0.0"
values = [
<<-EOT
grafana:
adminPassword: "StrongPassword123!"
persistence:
enabled: true
size: 10Gi
prometheus:
prometheusSpec:
retention: 15d
storageSpec:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
alertmanager:
alertmanagerSpec:
storage:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
EOT
]
depends_on = [aws_eks_node_group.main]
}
resource "helm_release" "ingress_nginx" {
name = "ingress-nginx"
repository = "https://kubernetes.github.io/ingress-nginx"
chart = "ingress-nginx"
namespace = kubernetes_namespace.ingress.metadata[0].name
version = "4.4.0"
set {
name = "controller.service.type"
value = "LoadBalancer"
}
set {
name = "controller.service.externalTrafficPolicy"
value = "Local"
}
set {
name = "controller.resources.requests.cpu"
value = "100m"
}
set {
name = "controller.resources.requests.memory"
value = "90Mi"
}
depends_on = [aws_eks_node_group.main]
}
resource "helm_release" "cert_manager" {
name = "cert-manager"
repository = "https://charts.jetstack.io"
chart = "cert-manager"
namespace = "cert-manager"
version = "v1.10.0"
create_namespace = true
set {
name = "installCRDs"
value = "true"
}
depends_on = [aws_eks_node_group.main]
}
resource "helm_release" "aws_load_balancer_controller" {
name = "aws-load-balancer-controller"
repository = "https://aws.github.io/eks-charts"
chart = "aws-load-balancer-controller"
namespace = "kube-system"
version = "1.4.6"
set {
name = "clusterName"
value = aws_eks_cluster.main.name
}
set {
name = "serviceAccount.create"
value = "true"
}
set {
name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.eks_nodes.arn
}
depends_on = [aws_eks_node_group.main]
}
# IAM Policy for AWS Load Balancer Controller
resource "aws_iam_policy" "load_balancer_controller" {
name = "${var.environment}-load-balancer-controller-policy"
description = "Policy for AWS Load Balancer Controller"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"iam:CreateServiceLinkedRole",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeInternetGateways",
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeInstances",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeTags",
"ec2:GetCoipPoolUsage",
"ec2:DescribeCoipPools",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeListenerCertificates",
"elasticloadbalancing:DescribeSSLPolicies",
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:DescribeTags"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"cognito-idp:DescribeUserPoolClient",
"acm:ListCertificates",
"acm:DescribeCertificate",
"iam:ListServerCertificates",
"iam:GetServerCertificate",
"waf-regional:GetWebACL",
"waf-regional:GetWebACLForResource",
"waf-regional:AssociateWebACL",
"waf-regional:DisassociateWebACL",
"wafv2:GetWebACL",
"wafv2:GetWebACLForResource",
"wafv2:AssociateWebACL",
"wafv2:DisassociateWebACL",
"shield:GetSubscriptionState",
"shield:DescribeProtection",
"shield:CreateProtection",
"shield:DeleteProtection"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"ec2:CreateSecurityGroup"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"ec2:CreateTags"
]
Resource = "arn:aws:ec2:*:*:security-group/*"
Condition = {
StringEquals = {
"ec2:CreateAction" = "CreateSecurityGroup"
}
Null = {
"aws:RequestTag/elbv2.k8s.aws/cluster" = "false"
}
}
},
{
Effect = "Allow"
Action = [
"ec2:CreateTags",
"ec2:DeleteTags"
]
Resource = "arn:aws:ec2:*:*:security-group/*"
Condition = {
Null = {
"aws:RequestTag/elbv2.k8s.aws/cluster" = "true"
"aws:ResourceTag/elbv2.k8s.aws/cluster" = "false"
}
}
},
{
Effect = "Allow"
Action = [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress",
"ec2:DeleteSecurityGroup"
]
Resource = "*"
Condition = {
StringEquals = {
"ec2:ResourceTag/elbv2.k8s.aws/cluster" = "true"
}
}
},
{
Effect = "Allow"
Action = [
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateTargetGroup"
]
Resource = "*"
Condition = {
Null = {
"aws:RequestTag/elbv2.k8s.aws/cluster" = "false"
}
}
},
{
Effect = "Allow"
Action = [
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:DeleteListener",
"elasticloadbalancing:CreateRule",
"elasticloadbalancing:DeleteRule"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"elasticloadbalancing:AddTags",
"elasticloadbalancing:RemoveTags"
]
Resource = [
"arn:aws:elasticloadbalancing:*:*:targetgroup/*/*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
]
Condition = {
Null = {
"aws:RequestTag/elbv2.k8s.aws/cluster" = "true"
"aws:ResourceTag/elbv2.k8s.aws/cluster" = "false"
}
}
},
{
Effect = "Allow"
Action = [
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:SetIpAddressType",
"elasticloadbalancing:SetSecurityGroups",
"elasticloadbalancing:SetSubnets",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:ModifyTargetGroup",
"elasticloadbalancing:ModifyTargetGroupAttributes",
"elasticloadbalancing:DeleteTargetGroup"
]
Resource = "*"
Condition = {
StringEquals = {
"elasticloadbalancing:ResourceTag/elbv2.k8s.aws/cluster" = "true"
}
}
},
{
Effect = "Allow"
Action = [
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:DeregisterTargets"
]
Resource = "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
},
{
Effect = "Allow"
Action = [
"elasticloadbalancing:SetWebAcl",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:AddListenerCertificates",
"elasticloadbalancing:RemoveListenerCertificates",
"elasticloadbalancing:ModifyRule"
]
Resource = "*"
}
]
})
}
resource "aws_iam_role_policy_attachment" "load_balancer_controller_policy" {
role = aws_iam_role.eks_nodes.name
policy_arn = aws_iam_policy.load_balancer_controller.arn
}
# Deploy a sample app for testing
resource "kubernetes_namespace" "demo" {
metadata {
name = "demo"
}
depends_on = [aws_eks_node_group.main]
}
resource "kubernetes_deployment" "demo" {
metadata {
name = "nginx-demo"
namespace = kubernetes_namespace.demo.metadata[0].name
labels = {
app = "nginx-demo"
}
}
spec {
replicas = 2
selector {
match_labels = {
app = "nginx-demo"
}
}
template {
metadata {
labels = {
app = "nginx-demo"
}
}
spec {
container {
image = "nginx:latest"
name = "nginx"
port {
container_port = 80
}
resources {
limits = {
cpu = "200m"
memory = "256Mi"
}
requests = {
cpu = "100m"
memory = "128Mi"
}
}
}
readiness_probe {
http_get {
path = "/"
port = 80
}
initial_delay_seconds = 10
period_seconds = 5
}
liveness_probe {
http_get {
path = "/"
port = 80
}
initial_delay_seconds = 15
period_seconds = 15
}
}
}
}
}
resource "kubernetes_service" "demo" {
metadata {
name = "nginx-demo"
namespace = kubernetes_namespace.demo.metadata[0].name
}
spec {
selector = {
app = kubernetes_deployment.demo.spec[0].template[0].metadata[0].labels.app
}
port {
port = 80
target_port = 80
}
type = "ClusterIP"
}
}
resource "kubernetes_ingress_v1" "demo" {
metadata {
name = "nginx-demo"
namespace = kubernetes_namespace.demo.metadata[0].name
annotations = {
"kubernetes.io/ingress.class" = "nginx"
"cert-manager.io/cluster-issuer" = "letsencrypt-prod"
"nginx.ingress.kubernetes.io/ssl-redirect" = "true"
}
}
spec {
rule {
host = "demo.example.com"
http {
path {
path = "/"
path_type = "Prefix"
backend {
service {
name = kubernetes_service.demo.metadata[0].name
port {
number = 80
}
}
}
}
}
}
tls {
hosts = ["demo.example.com"]
secret_name = "demo-tls"
}
}
depends_on = [helm_release.ingress_nginx, helm_release.cert_manager]
}
# Create ClusterIssuer for Let's Encrypt certificates
resource "kubernetes_manifest" "cluster_issuer" {
manifest = {
apiVersion = "cert-manager.io/v1"
kind = "ClusterIssuer"
metadata = {
name = "letsencrypt-prod"
}
spec = {
acme = {
server = "https://acme-v02.api.letsencrypt.org/directory"
email = "[email protected]"
privateKeySecretRef = {
name = "letsencrypt-prod"
}
solvers = [
{
http01 = {
ingress = {
class = "nginx"
}
}
}
]
}
}
}
depends_on = [helm_release.cert_manager]
}
# Setup External DNS
resource "helm_release" "external_dns" {
name = "external-dns"
repository = "https://charts.bitnami.com/bitnami"
chart = "external-dns"
namespace = "kube-system"
version = "6.11.0"
set {
name = "provider"
value = "aws"
}
set {
name = "aws.region"
value = var.region
}
set {
name = "domainFilters[0]"
value = "example.com"
}
set {
name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.eks_nodes.arn
}
depends_on = [aws_eks_node_group.main]
}
# Create IAM policy for External DNS
resource "aws_iam_policy" "external_dns" {
name = "${var.environment}-external-dns-policy"
description = "Policy for External DNS"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"route53:ChangeResourceRecordSets"
]
Resource = [
"arn:aws:route53:::hostedzone/*"
]
},
{
Effect = "Allow"
Action = [
"route53:ListHostedZones",
"route53:ListResourceRecordSets"
]
Resource = [
"*"
]
}
]
})
}
resource "aws_iam_role_policy_attachment" "external_dns_policy" {
role = aws_iam_role.eks_nodes.name
policy_arn = aws_iam_policy.external_dns.arn
}
# Configure persistent volumes with EBS CSI driver
resource "helm_release" "aws_ebs_csi_driver" {
name = "aws-ebs-csi-driver"
repository = "https://kubernetes-sigs.github.io/aws-ebs-csi-driver"
chart = "aws-ebs-csi-driver"
namespace = "kube-system"
version = "2.16.0"
set {
name = "controller.serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.eks_nodes.arn
}
depends_on = [aws_eks_node_group.main]
}
resource "aws_iam_policy" "ebs_csi_driver" {
name = "${var.environment}-ebs-csi-driver-policy"
description = "Policy for EBS CSI Driver"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ec2:CreateSnapshot",
"ec2:AttachVolume",
"ec2:DetachVolume",
"ec2:ModifyVolume",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInstances",
"ec2:DescribeSnapshots",
"ec2:DescribeTags",
"ec2:DescribeVolumes",
"ec2:DescribeVolumesModifications"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"ec2:CreateTags"
]
Resource = [
"arn:aws:ec2:*:*:volume/*",
"arn:aws:ec2:*:*:snapshot/*"
]
Condition = {
StringEquals = {
"ec2:CreateAction" = [
"CreateVolume",
"CreateSnapshot"
]
}
}
},
{
Effect = "Allow"
Action = [
"ec2:DeleteTags"
]
Resource = [
"arn:aws:ec2:*:*:volume/*",
"arn:aws:ec2:*:*:snapshot/*"
]
},
{
Effect = "Allow"
Action = [
"ec2:CreateVolume"
]
Resource = "*"
Condition = {
StringLike = {
"aws:RequestTag/kubernetes.io/cluster/*" = "owned"
}
}
},
{
Effect = "Allow"
Action = [
"ec2:CreateVolume"
]
Resource = "*"
Condition = {
StringLike = {
"aws:RequestTag/CSIVolumeName" = "*"
}
}
},
{
Effect = "Allow"
Action = [
"ec2:DeleteVolume"
]
Resource = "*"
Condition = {
StringLike = {
"ec2:ResourceTag/CSIVolumeName" = "*"
}
}
},
{
Effect = "Allow"
Action = [
"ec2:DeleteVolume"
]
Resource = "*"
Condition = {
StringLike = {
"ec2:ResourceTag/kubernetes.io/cluster/*" = "owned"
}
}
},
{
Effect = "Allow"
Action = [
"ec2:DeleteSnapshot"
]
Resource = "*"
Condition = {
StringLike = {
"ec2:ResourceTag/CSIVolumeSnapshotName" = "*"
}
}
},
{
Effect = "Allow"
Action = [
"ec2:DeleteSnapshot"
]
Resource = "*"
Condition = {
StringLike = {
"ec2:ResourceTag/kubernetes.io/cluster/*" = "owned"
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "ebs_csi_driver_policy" {
role = aws_iam_role.eks_nodes.name
policy_arn = aws_iam_policy.ebs_csi_driver.arn
}
# Create storage class for gp3 EBS volumes
resource "kubernetes_storage_class" "gp3" {
metadata {
name = "gp3"
annotations = {
"storageclass.kubernetes.io/is-default-class" = "true"
}
}
storage_provisioner = "ebs.csi.aws.com"
reclaim_policy = "Delete"
allow_volume_expansion = true
volume_binding_mode = "WaitForFirstConsumer"
parameters = {
type = "gp3"
fsType = "ext4"
}
depends_on = [helm_release.aws_ebs_csi_driver]
}
# Backup solution with Velero
resource "kubernetes_namespace" "velero" {
metadata {
name = "velero"
}
depends_on = [aws_eks_node_group.main]
}
resource "aws_s3_bucket" "velero" {
bucket = "${var.environment}-velero-backup-${random_id.suffix.hex}"
tags = {
Name = "${var.environment}-velero-backup-bucket"
}
}
resource "aws_s3_bucket_versioning" "velero" {
bucket = aws_s3_bucket.velero.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_lifecycle_configuration" "velero" {
bucket = aws_s3_bucket.velero.id
rule {
id = "expire-old-backups"
status = "Enabled"
expiration {
days = 90
}
noncurrent_version_expiration {
noncurrent_days = 30
}
}
}
resource "aws_iam_policy" "velero" {
name = "${var.environment}-velero-policy"
description = "Policy for Velero backup and restore"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ec2:DescribeVolumes",
"ec2:DescribeSnapshots",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:CreateSnapshot",
"ec2:DeleteSnapshot"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObject",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts"
]
Resource = [
"${aws_s3_bucket.velero.arn}/*"
]
},
{
Effect = "Allow"
Action = [
"s3:ListBucket"
]
Resource = [
"${aws_s3_bucket.velero.arn}"
]
}
]
})
}
resource "aws_iam_role_policy_attachment" "velero_policy" {
role = aws_iam_role.eks_nodes.name
policy_arn = aws_iam_policy.velero.arn
}
resource "helm_release" "velero" {
name = "velero"
repository = "https://vmware-tanzu.github.io/helm-charts"
chart = "velero"
namespace = kubernetes_namespace.velero.metadata[0].name
version = "4.0.0"
set {
name = "configuration.provider"
value = "aws"
}
set {
name = "configuration.backupStorageLocation.bucket"
value = aws_s3_bucket.velero.id
}
set {
name = "configuration.backupStorageLocation.config.region"
value = var.region
}
set {
name = "initContainers[0].name"
value = "velero-plugin-for-aws"
}
set {
name = "initContainers[0].image"
value = "velero/velero-plugin-for-aws:v1.5.0"
}
set {
name = "initContainers[0].volumeMounts[0].mountPath"
value = "/target"
}
set {
name = "initContainers[0].volumeMounts[0].name"
value = "plugins"
}
set {
name = "serviceAccount.server.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.eks_nodes.arn
}
set {
name = "snapshotsEnabled"
value = "true"
}
set {
name = "deployRestic"
value = "true"
}
set {
name = "restic.privileged"
value = "true"
}
depends_on = [aws_eks_node_group.main, aws_s3_bucket.velero]
}
# Setup periodic backups with Velero
resource "kubernetes_manifest" "backup_schedule" {
manifest = {
apiVersion = "velero.io/v1"
kind = "Schedule"
metadata = {
name = "daily-backup"
namespace = kubernetes_namespace.velero.metadata[0].name
}
spec = {
schedule = "0 1 * * *"
template = {
ttl = "720h"
includedNamespaces = ["*"]
excludedNamespaces = ["kube-system", "velero"]
includeClusterResources = true
storageLocation = "default"
volumeSnapshotLocations = ["default"]
}
}
}
depends_on = [helm_release.velero]
}
# NetworkPolicy to restrict communication between namespaces
resource "kubernetes_network_policy" "default_deny" {
metadata {
name = "default-deny"
namespace = kubernetes_namespace.demo.metadata[0].name
}
spec {
pod_selector {}
policy_types = ["Ingress", "Egress"]
}
depends_on = [aws_eks_node_group.main]
}
resource "kubernetes_network_policy" "allow_ingress" {
metadata {
name = "allow-ingress-controllers"
namespace = kubernetes_namespace.demo.metadata[0].name
}
spec {
pod_selector {
match_labels = {
app = "nginx-demo"
}
}
ingress {
from {
namespace_selector {
match_labels = {
"kubernetes.io/metadata.name" = "ingress-nginx"
}
}
}
}
policy_types = ["Ingress"]
}
depends_on = [aws_eks_node_group.main, kubernetes_namespace.ingress]
}
resource "kubernetes_network_policy" "allow_monitoring" {
metadata {
name = "allow-monitoring"
namespace = kubernetes_namespace.demo.metadata[0].name
}
spec {
pod_selector {}
ingress {
from {
namespace_selector {
match_labels = {
"kubernetes.io/metadata.name" = "monitoring"
}
}
}
}
policy_types = ["Ingress"]
}
depends_on = [aws_eks_node_group.main, kubernetes_namespace.monitoring]
}
# Output values
output "cluster_endpoint" {
description = "EKS cluster endpoint"
value = aws_eks_cluster.main.endpoint
}
output "cluster_security_group_id" {
description = "Security group ID attached to the EKS cluster"
value = aws_security_group.eks_cluster.id
}
output "cluster_name" {
description = "EKS cluster name"
value = aws_eks_cluster.main.name
}
output "cluster_certificate_authority_data" {
description = "Base64 encoded certificate data required to communicate with the cluster"
value = aws_eks_cluster.main.certificate_authority[0].data
sensitive = true
}
output "cluster_oidc_issuer_url" {
description = "The URL on the EKS cluster for OpenID Connect"
value = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
output "node_security_group_id" {
description = "Security group ID attached to the EKS nodes"
value = aws_security_group.eks_nodes.id
}
output "kubectl_config_command" {
description = "kubectl configuration command"
value = "aws eks update-kubeconfig --name ${aws_eks_cluster.main.name} --region ${var.region}"
}
output "prometheus_grafana_url" {
description = "URL for Grafana dashboard"
value = "https://grafana.example.com"
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.31.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.23.0"
}
helm = {
source = "hashicorp/helm"
version = "~> 2.11.0"
}
tls = {
source = "hashicorp/tls"
version = "~> 4.0.4"
}
}
backend "s3" {
bucket = "terraform-k8s-state"
key = "terraform/k8s/state.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
provider "aws" {
region = var.region
default_tags {
tags = {
Environment = var.environment
Project = "kubernetes-cluster"
ManagedBy = "terraform"
}
}
}
# Variables
variable "region" {
description = "AWS region to deploy the cluster"
type = string
default = "us-west-2"
}
variable "environment" {
description = "Environment name (e.g., prod, staging)"
type = string
default = "prod"
}
variable "cluster_name" {
description = "Name of the EKS cluster"
type = string
default = "resilient-k8s-cluster"
}
variable "cluster_version" {
description = "Kubernetes version"
type = string
default = "1.28"
}
variable "vpc_cidr" {
description = "CIDR block for the VPC"
type = string
default = "10.0.0.0/16"
}
variable "availability_zones" {
description = "List of availability zones"
type = list(string)
default = ["us-west-2a", "us-west-2b", "us-west-2c"]
}
variable "node_groups" {
description = "Node group configuration"
type = map(object({
instance_types = list(string)
min_size = number
max_size = number
desired_size = number
disk_size = number
}))
default = {
critical = {
instance_types = ["m5.large", "m5a.large", "m5d.large"]
min_size = 3
max_size = 5
desired_size = 3
disk_size = 100
},
applications = {
instance_types = ["c5.xlarge", "c5a.xlarge", "c5d.xlarge"]
min_size = 3
max_size = 10
desired_size = 3
disk_size = 100
}
}
}
# Network Infrastructure
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${var.cluster_name}-vpc"
}
}
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index)
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.cluster_name}-private-${var.availability_zones[count.index]}"
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/internal-elb" = "1"
}
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index + length(var.availability_zones))
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.cluster_name}-public-${var.availability_zones[count.index]}"
"kubernetes.io/cluster/${var.cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.cluster_name}-igw"
}
}
resource "aws_eip" "nat" {
count = length(var.availability_zones)
domain = "vpc"
tags = {
Name = "${var.cluster_name}-nat-eip-${count.index}"
}
}
resource "aws_nat_gateway" "main" {
count = length(var.availability_zones)
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.cluster_name}-nat-${count.index}"
}
depends_on = [aws_internet_gateway.main]
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.cluster_name}-public-rt"
}
}
resource "aws_route_table" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = {
Name = "${var.cluster_name}-private-rt-${count.index}"
}
}
resource "aws_route_table_association" "public" {
count = length(var.availability_zones)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = length(var.availability_zones)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
# Security Groups
resource "aws_security_group" "cluster" {
name = "${var.cluster_name}-cluster-sg"
description = "Security group for Kubernetes cluster control plane"
vpc_id = aws_vpc.main.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.cluster_name}-cluster-sg"
}
}
resource "aws_security_group" "nodes" {
name = "${var.cluster_name}-node-sg"
description = "Security group for Kubernetes cluster nodes"
vpc_id = aws_vpc.main.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.cluster_name}-node-sg"
}
}
resource "aws_security_group_rule" "cluster_ingress_node_https" {
description = "Allow nodes to communicate with the cluster API Server"
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
security_group_id = aws_security_group.cluster.id
source_security_group_id = aws_security_group.nodes.id
}
resource "aws_security_group_rule" "nodes_ingress_self" {
description = "Allow nodes to communicate with each other"
type = "ingress"
from_port = 0
to_port = 65535
protocol = "-1"
security_group_id = aws_security_group.nodes.id
source_security_group_id = aws_security_group.nodes.id
}
resource "aws_security_group_rule" "nodes_ingress_cluster" {
description = "Allow worker Kubelets and pods to receive communication from the cluster control plane"
type = "ingress"
from_port = 1025
to_port = 65535
protocol = "tcp"
security_group_id = aws_security_group.nodes.id
source_security_group_id = aws_security_group.cluster.id
}
# IAM Roles for EKS
resource "aws_iam_role" "cluster" {
name = "${var.cluster_name}-cluster-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "eks.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSClusterPolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.cluster.name
}
resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSVPCResourceController" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController"
role = aws_iam_role.cluster.name
}
resource "aws_iam_role" "nodes" {
name = "${var.cluster_name}-node-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_role_policy_attachment" "nodes_AmazonEKSWorkerNodePolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
role = aws_iam_role.nodes.name
}
resource "aws_iam_role_policy_attachment" "nodes_AmazonEKS_CNI_Policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
role = aws_iam_role.nodes.name
}
resource "aws_iam_role_policy_attachment" "nodes_AmazonEC2ContainerRegistryReadOnly" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
role = aws_iam_role.nodes.name
}
resource "aws_iam_role_policy_attachment" "nodes_AmazonSSMManagedInstanceCore" {
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
role = aws_iam_role.nodes.name
}
# EKS Cluster
resource "aws_eks_cluster" "main" {
name = var.cluster_name
role_arn = aws_iam_role.cluster.arn
version = var.cluster_version
vpc_config {
subnet_ids = concat(aws_subnet.private[*].id, aws_subnet.public[*].id)
security_group_ids = [aws_security_group.cluster.id]
endpoint_private_access = true
endpoint_public_access = true
}
encryption_config {
provider {
key_arn = aws_kms_key.eks.arn
}
resources = ["secrets"]
}
enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
depends_on = [
aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy,
aws_iam_role_policy_attachment.cluster_AmazonEKSVPCResourceController,
aws_cloudwatch_log_group.eks
]
}
# KMS Key for EKS encryption
resource "aws_kms_key" "eks" {
description = "EKS Secret Encryption Key"
deletion_window_in_days = 7
enable_key_rotation = true
tags = {
Name = "${var.cluster_name}-kms"
}
}
# CloudWatch Logs
resource "aws_cloudwatch_log_group" "eks" {
name = "/aws/eks/${var.cluster_name}/cluster"
retention_in_days = 90
}
# EKS Node Groups
resource "aws_eks_node_group" "main" {
for_each = var.node_groups
cluster_name = aws_eks_cluster.main.name
node_group_name = "${var.cluster_name}-${each.key}"
node_role_arn = aws_iam_role.nodes.arn
subnet_ids = aws_subnet.private[*].id
ami_type = "AL2_x86_64"
capacity_type = "ON_DEMAND"
instance_types = each.value.instance_types
disk_size = each.value.disk_size
scaling_config {
desired_size = each.value.desired_size
min_size = each.value.min_size
max_size = each.value.max_size
}
update_config {
max_unavailable = 1
}
remote_access {
ec2_ssh_key = aws_key_pair.eks.key_name
source_security_group_ids = [aws_security_group.cluster_ssh.id]
}
labels = {
"role" = each.key
}
tags = {
Name = "${var.cluster_name}-${each.key}"
}
depends_on = [
aws_iam_role_policy_attachment.nodes_AmazonEKSWorkerNodePolicy,
aws_iam_role_policy_attachment.nodes_AmazonEKS_CNI_Policy,
aws_iam_role_policy_attachment.nodes_AmazonEC2ContainerRegistryReadOnly,
aws_iam_role_policy_attachment.nodes_AmazonSSMManagedInstanceCore
]
lifecycle {
create_before_destroy = true
}
}
# SSH Key for Node Access
resource "tls_private_key" "eks" {
algorithm = "RSA"
rsa_bits = 4096
}
resource "aws_key_pair" "eks" {
key_name = "${var.cluster_name}-keypair"
public_key = tls_private_key.eks.public_key_openssh
}
resource "aws_secretsmanager_secret" "ssh_key" {
name = "${var.cluster_name}-ssh-key"
recovery_window_in_days = 0
}
resource "aws_secretsmanager_secret_version" "ssh_key" {
secret_id = aws_secretsmanager_secret.ssh_key.id
secret_string = tls_private_key.eks.private_key_pem
}
resource "aws_security_group" "cluster_ssh" {
name = "${var.cluster_name}-ssh-sg"
description = "Security group for SSH access to EKS nodes"
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"] # Restrict to VPN/bastion hosts in production
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.cluster_name}-ssh-sg"
}
}
# Kubernetes Provider Configuration
provider "kubernetes" {
host = aws_eks_cluster.main.endpoint
cluster_ca_certificate = base64decode(aws_eks_cluster.main.certificate_authority[0].data)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
args = ["eks", "get-token", "--cluster-name", aws_eks_cluster.main.name]
command = "aws"
}
}
provider "helm" {
kubernetes {
host = aws_eks_cluster.main.endpoint
cluster_ca_certificate = base64decode(aws_eks_cluster.main.certificate_authority[0].data)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
args = ["eks", "get-token", "--cluster-name", aws_eks_cluster.main.name]
command = "aws"
}
}
}
# Kubernetes Add-ons
# AWS Load Balancer Controller
resource "aws_iam_policy" "aws_load_balancer_controller" {
name = "${var.cluster_name}-alb-controller-policy"
description = "Policy for AWS Load Balancer Controller"
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:CreateServiceLinkedRole"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:AWSServiceName": "elasticloadbalancing.amazonaws.com"
}
}
},
{
"Effect": "Allow",
"Action": [
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInternetGateways",
"ec2:DescribeVpcs",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeInstances",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeTags",
"ec2:GetCoipPoolUsage",
"ec2:DescribeCoipPools",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeListenerCertificates",
"elasticloadbalancing:DescribeSSLPolicies",
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:DescribeTags"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cognito-idp:DescribeUserPoolClient",
"acm:ListCertificates",
"acm:DescribeCertificate",
"iam:ListServerCertificates",
"iam:GetServerCertificate",
"waf-regional:GetWebACL",
"waf-regional:GetWebACLForResource",
"waf-regional:AssociateWebACL",
"waf-regional:DisassociateWebACL",
"wafv2:GetWebACL",
"wafv2:GetWebACLForResource",
"wafv2:AssociateWebACL",
"wafv2:DisassociateWebACL",
"shield:GetSubscriptionState",
"shield:DescribeProtection",
"shield:CreateProtection",
"shield:DeleteProtection"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateSecurityGroup"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateTags"
],
"Resource": "arn:aws:ec2:*:*:security-group/*",
"Condition": {
"StringEquals": {
"ec2:CreateAction": "CreateSecurityGroup"
},
"Null": {
"aws:RequestTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateTags",
"ec2:DeleteTags"
],
"Resource": "arn:aws:ec2:*:*:security-group/*",
"Condition": {
"Null": {
"aws:RequestTag/elbv2.k8s.aws/cluster": "true",
"aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress",
"ec2:DeleteSecurityGroup"
],
"Resource": "*",
"Condition": {
"Null": {
"aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateTargetGroup"
],
"Resource": "*",
"Condition": {
"Null": {
"aws:RequestTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:DeleteListener",
"elasticloadbalancing:CreateRule",
"elasticloadbalancing:DeleteRule"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:AddTags",
"elasticloadbalancing:RemoveTags"
],
"Resource": [
"arn:aws:elasticloadbalancing:*:*:targetgroup/*/*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*",
"arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
],
"Condition": {
"Null": {
"aws:RequestTag/elbv2.k8s.aws/cluster": "true",
"aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:AddTags",
"elasticloadbalancing:RemoveTags"
],
"Resource": [
"arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*",
"arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*",
"arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*",
"arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*"
]
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:SetIpAddressType",
"elasticloadbalancing:SetSecurityGroups",
"elasticloadbalancing:SetSubnets",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:ModifyTargetGroup",
"elasticloadbalancing:ModifyTargetGroupAttributes",
"elasticloadbalancing:DeleteTargetGroup"
],
"Resource": "*",
"Condition": {
"Null": {
"aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
}
}
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:DeregisterTargets"
],
"Resource": "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:SetWebAcl",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:AddListenerCertificates",
"elasticloadbalancing:RemoveListenerCertificates",
"elasticloadbalancing:ModifyRule"
],
"Resource": "*"
}
]
})
}
resource "aws_iam_role" "aws_load_balancer_controller" {
name = "${var.cluster_name}-alb-controller-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}:sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "aws_load_balancer_controller" {
policy_arn = aws_iam_policy.aws_load_balancer_controller.arn
role = aws_iam_role.aws_load_balancer_controller.name
}
resource "helm_release" "aws_load_balancer_controller" {
name = "aws-load-balancer-controller"
repository = "https://aws.github.io/eks-charts"
chart = "aws-load-balancer-controller"
namespace = "kube-system"
version = "1.6.0"
set {
name = "clusterName"
value = aws_eks_cluster.main.name
}
set {
name = "serviceAccount.create"
value = "true"
}
set {
name = "serviceAccount.name"
value = "aws-load-balancer-controller"
}
set {
name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.aws_load_balancer_controller.arn
}
depends_on = [
aws_eks_node_group.main,
kubernetes_config_map.aws_auth
]
}
# AWS Auth Config Map
resource "kubernetes_config_map" "aws_auth" {
metadata {
name = "aws-auth"
namespace = "kube-system"
}
data = {
mapRoles = yamlencode([
{
rolearn = aws_iam_role.nodes.arn
username = "system:node:{{EC2PrivateDNSName}}"
groups = ["system:bootstrappers", "system:nodes"]
}
])
}
depends_on = [
aws_eks_cluster.main
]
}
# Cluster Autoscaler
resource "aws_iam_role" "cluster_autoscaler" {
name = "${var.cluster_name}-cluster-autoscaler-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}:sub": "system:serviceaccount:kube-system:cluster-autoscaler"
}
}
}
]
})
}
resource "aws_iam_policy" "cluster_autoscaler" {
name = "${var.cluster_name}-cluster-autoscaler-policy"
description = "Policy for Cluster Autoscaler"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeAutoScalingInstances",
"autoscaling:DescribeLaunchConfigurations",
"autoscaling:DescribeTags",
"autoscaling:SetDesiredCapacity",
"autoscaling:TerminateInstanceInAutoScalingGroup",
"ec2:DescribeLaunchTemplateVersions"
],
Resource = "*"
}
]
})
}
resource "aws_iam_role_policy_attachment" "cluster_autoscaler" {
policy_arn = aws_iam_policy.cluster_autoscaler.arn
role = aws_iam_role.cluster_autoscaler.name
}
resource "helm_release" "cluster_autoscaler" {
name = "cluster-autoscaler"
repository = "https://kubernetes.github.io/autoscaler"
chart = "cluster-autoscaler"
namespace = "kube-system"
version = "9.29.0"
set {
name = "autoDiscovery.clusterName"
value = aws_eks_cluster.main.name
}
set {
name = "awsRegion"
value = var.region
}
set {
name = "rbac.serviceAccount.create"
value = "true"
}
set {
name = "rbac.serviceAccount.name"
value = "cluster-autoscaler"
}
set {
name = "rbac.serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.cluster_autoscaler.arn
}
# Improve scaling performance and reduce throttling
set {
name = "extraArgs.scan-interval"
value = "30s"
}
set {
name = "extraArgs.max-node-provision-time"
value = "15m0s"
}
set {
name = "extraArgs.scale-down-delay-after-add"
value = "10m"
}
depends_on = [
aws_eks_node_group.main,
kubernetes_config_map.aws_auth
]
}
# External DNS
resource "aws_iam_role" "external_dns" {
name = "${var.cluster_name}-external-dns-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}:sub": "system:serviceaccount:kube-system:external-dns"
}
}
}
]
})
}
resource "aws_iam_policy" "external_dns" {
name = "${var.cluster_name}-external-dns-policy"
description = "Policy for External DNS"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"route53:ChangeResourceRecordSets"
]
Resource = [
"arn:aws:route53:::hostedzone/*"
]
},
{
Effect = "Allow"
Action = [
"route53:ListHostedZones",
"route53:ListResourceRecordSets"
]
Resource = [
"*"
]
}
]
})
}
resource "aws_iam_role_policy_attachment" "external_dns" {
policy_arn = aws_iam_policy.external_dns.arn
role = aws_iam_role.external_dns.name
}
resource "helm_release" "external_dns" {
name = "external-dns"
repository = "https://charts.bitnami.com/bitnami"
chart = "external-dns"
namespace = "kube-system"
version = "6.28.1"
set {
name = "provider"
value = "aws"
}
set {
name = "aws.region"
value = var.region
}
set {
name = "serviceAccount.create"
value = "true"
}
set {
name = "serviceAccount.name"
value = "external-dns"
}
set {
name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.external_dns.arn
}
set {
name = "policy"
value = "sync"
}
set {
name = "txtOwnerId"
value = var.cluster_name
}
depends_on = [
aws_eks_node_group.main,
kubernetes_config_map.aws_auth
]
}
# Metrics Server
resource "helm_release" "metrics_server" {
name = "metrics-server"
repository = "https://kubernetes-sigs.github.io/metrics-server/"
chart = "metrics-server"
namespace = "kube-system"
version = "3.11.0"
set {
name = "args[0]"
value = "--kubelet-insecure-tls"
}
set {
name = "args[1]"
value = "--kubelet-preferred-address-types=InternalIP"
}
depends_on = [
aws_eks_node_group.main,
kubernetes_config_map.aws_auth
]
}
# Prometheus and Grafana for monitoring
resource "kubernetes_namespace" "monitoring" {
metadata {
name = "monitoring"
}
depends_on = [
aws_eks_cluster.main
]
}
resource "helm_release" "prometheus_operator" {
name = "prometheus"
repository = "https://prometheus-community.github.io/helm-charts"
chart = "kube-prometheus-stack"
namespace = kubernetes_namespace.monitoring.metadata[0].name
version = "55.5.0"
set {
name = "grafana.enabled"
value = "true"
}
set {
name = "grafana.adminPassword"
value = "changeme" # In production, use aws_ssm_parameter or kubernetes_secret
}
set {
name = "prometheus.prometheusSpec.retention"
value = "15d"
}
set {
name = "prometheus.prometheusSpec.resources.requests.cpu"
value = "500m"
}
set {
name = "prometheus.prometheusSpec.resources.requests.memory"
value = "2Gi"
}
set {
name = "prometheus.prometheusSpec.resources.limits.cpu"
value = "1000m"
}
set {
name = "prometheus.prometheusSpec.resources.limits.memory"
value = "4Gi"
}
set {
name = "prometheusOperator.tls.enabled"
value = "true"
}
depends_on = [
aws_eks_node_group.main,
kubernetes_config_map.aws_auth
]
}
# Create an EFS filesystem for persistent storage
resource "aws_efs_file_system" "eks" {
creation_token = "${var.cluster_name}-efs"
encrypted = true
tags = {
Name = "${var.cluster_name}-efs"
}
}
resource "aws_security_group" "efs" {
name = "${var.cluster_name}-efs-sg"
description = "Allow traffic from EKS nodes to EFS"
vpc_id = aws_vpc.main.id
ingress {
description = "Allow NFS traffic from EKS nodes"
from_port = 2049
to_port = 2049
protocol = "tcp"
security_groups = [aws_security_group.nodes.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.cluster_name}-efs-sg"
}
}
resource "aws_efs_mount_target" "eks" {
count = length(var.availability_zones)
file_system_id = aws_efs_file_system.eks.id
subnet_id = aws_subnet.private[count.index].id
security_groups = [aws_security_group.efs.id]
}
# EFS CSI Driver
resource "aws_iam_role" "efs_csi_driver" {
name = "${var.cluster_name}-efs-csi-driver-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}:sub": "system:serviceaccount:kube-system:efs-csi-controller-sa"
}
}
}
]
})
}
resource "aws_iam_policy" "efs_csi_driver" {
name = "${var.cluster_name}-efs-csi-driver-policy"
description = "Policy for EFS CSI Driver"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"elasticfilesystem:DescribeAccessPoints",
"elasticfilesystem:DescribeFileSystems",
"elasticfilesystem:DescribeMountTargets",
"ec2:DescribeAvailabilityZones"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"elasticfilesystem:CreateAccessPoint"
]
Resource = "*"
Condition = {
StringLike = {
"aws:RequestTag/kubernetes.io/cluster/*": "owned"
}
}
},
{
Effect = "Allow"
Action = [
"elasticfilesystem:DeleteAccessPoint"
]
Resource = "*"
Condition = {
StringEquals = {
"aws:ResourceTag/kubernetes.io/cluster/${var.cluster_name}": "owned"
}
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "efs_csi_driver" {
policy_arn = aws_iam_policy.efs_csi_driver.arn
role = aws_iam_role.efs_csi_driver.name
}
resource "helm_release" "efs_csi_driver" {
name = "aws-efs-csi-driver"
repository = "https://kubernetes-sigs.github.io/aws-efs-csi-driver/"
chart = "aws-efs-csi-driver"
namespace = "kube-system"
version = "2.5.2"
set {
name = "controller.serviceAccount.create"
value = "true"
}
set {
name = "controller.serviceAccount.name"
value = "efs-csi-controller-sa"
}
set {
name = "controller.serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.efs_csi_driver.arn
}
depends_on = [
aws_eks_node_group.main,
aws_efs_mount_target.eks,
kubernetes_config_map.aws_auth
]
}
# Create storage class for EFS
resource "kubernetes_storage_class" "efs" {
metadata {
name = "efs-sc"
}
storage_provisioner = "efs.csi.aws.com"
reclaim_policy = "Retain"
parameters = {
provisioningMode = "efs-ap"
fileSystemId = aws_efs_file_system.eks.id
directoryPerms = "700"
}
mount_options = ["tls"]
depends_on = [
helm_release.efs_csi_driver
]
}
# Install Cert-Manager for TLS certificate management
resource "helm_release" "cert_manager" {
name = "cert-manager"
repository = "https://charts.jetstack.io"
chart = "cert-manager"
namespace = "cert-manager"
version = "v1.13.3"
create_namespace = true
set {
name = "installCRDs"
value = "true"
}
set {
name = "prometheus.enabled"
value = "true"
}
depends_on = [
aws_eks_node_group.main,
kubernetes_config_map.aws_auth
]
}
# Istio service mesh for advanced networking
resource "kubernetes_namespace" "istio_system" {
metadata {
name = "istio-system"
labels = {
istio-injection = "disabled"
}
}
depends_on = [
aws_eks_cluster.main
]
}
resource "helm_release" "istio_base" {
name = "istio-base"
repository = "https://istio-release.storage.googleapis.com/charts"
chart = "base"
namespace = kubernetes_namespace.istio_system.metadata[0].name
version = "1.20.0"
depends_on = [
aws_eks_node_group.main,
kubernetes_config_map.aws_auth
]
}
resource "helm_release" "istiod" {
name = "istiod"
repository = "https://istio-release.storage.googleapis.com/charts"
chart = "istiod"
namespace = kubernetes_namespace.istio_system.metadata[0].name
version = "1.20.0"
set {
name = "pilot.resources.requests.cpu"
value = "500m"
}
set {
name = "pilot.resources.requests.memory"
value = "2Gi"
}
set {
name = "global.proxy.resources.requests.cpu"
value = "100m"
}
set {
name = "global.proxy.resources.requests.memory"
value = "128Mi"
}
set {
name = "global.proxy.resources.limits.cpu"
value = "500m"
}
set {
name = "global.proxy.resources.limits.memory"
value = "512Mi"
}
depends_on = [
helm_release.istio_base
]
}
resource "helm_release" "istio_ingress" {
name = "istio-ingress"
repository = "https://istio-release.storage.googleapis.com/charts"
chart = "gateway"
namespace = kubernetes_namespace.istio_system.metadata[0].name
version = "1.20.0"
set {
name = "service.type"
value = "LoadBalancer"
}
set {
name = "service.annotations.service\\.beta\\.kubernetes\\.io/aws-load-balancer-type"
value = "nlb"
}
set {
name = "resources.requests.cpu"
value = "100m"
}
set {
name = "resources.requests.memory"
value = "128Mi"
}
set {
name = "resources.limits.cpu"
value = "500m"
}
set {
name = "resources.limits.memory"
value = "512Mi"
}
depends_on = [
helm_release.istiod
]
}
# Velero for backup and restore
resource "kubernetes_namespace" "velero" {
metadata {
name = "velero"
}
depends_on = [
aws_eks_cluster.main
]
}
resource "aws_s3_bucket" "velero" {
bucket = "${var.cluster_name}-velero-backups"
tags = {
Name = "${var.cluster_name}-velero-backups"
}
}
resource "aws_s3_bucket_versioning" "velero" {
bucket = aws_s3_bucket.velero.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "velero" {
bucket = aws_s3_bucket.velero.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_iam_role" "velero" {
name = "${var.cluster_name}-velero-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"${replace(aws_eks_cluster.main.identity[0].oidc[0].issuer, "https://", "")}:sub": "system:serviceaccount:velero:velero"
}
}
}
]
})
}
resource "aws_iam_policy" "velero" {
name = "${var.cluster_name}-velero-policy"
description = "Policy for Velero backup and restore"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ec2:DescribeVolumes",
"ec2:DescribeSnapshots",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:CreateSnapshot",
"ec2:DeleteSnapshot"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObject",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts"
]
Resource = [
"${aws_s3_bucket.velero.arn}/*"
]
},
{
Effect = "Allow"
Action = [
"s3:ListBucket"
]
Resource = [
"${aws_s3_bucket.velero.arn}"
]
}
]
})
}
resource "aws_iam_role_policy_attachment" "velero" {
policy_arn = aws_iam_policy.velero.arn
role = aws_iam_role.velero.name
}
resource "helm_release" "velero" {
name = "velero"
repository = "https://vmware-tanzu.github.io/helm-charts"
chart = "velero"
namespace = kubernetes_namespace.velero.metadata[0].name
version = "5.1.3"
set {
name = "credentials.useSecret"
value = "false"
}
set {
name = "serviceAccount.server.annotations.eks\\.amazonaws\\.com/role-arn"
value = aws_iam_role.velero.arn
}
set {
name = "configuration.provider"
value = "aws"
}
set {
name = "configuration.backupStorageLocation.bucket"
value = aws_s3_bucket.velero.id
}
set {
name = "configuration.backupStorageLocation.config.region"
value = var.region
}
set {
name = "configuration.volumeSnapshotLocation.config.region"
value = var.region
}
set {
name = "initContainers[0].name"
value = "velero-plugin-for-aws"
}
set {
name = "initContainers[0].image"
value = "velero/velero-plugin-for-aws:v1.8.0"
}
set {
name = "initContainers[0].volumeMounts[0].mountPath"
value = "/target"
}
set {
name = "initContainers[0].volumeMounts[0].name"
value = "plugins"
}
# Configure backup schedule
set {
name = "schedules.daily-backup.schedule"
value = "0 1 * * *" # 1:00 AM UTC every day
}
set {
name = "schedules.daily-backup.template.ttl"
value = "168h" # 7 days
}
set {
name = "schedules.daily-backup.template.includedNamespaces"
value = "{*}"
}
set {
name = "schedules.daily-backup.template.snapshotVolumes"
value = "true"
}
depends_on = [
aws_eks_node_group.main,
kubernetes_config_map.aws_auth,
aws_s3_bucket.velero
]
}
# AWS Caller Identity Data Source
data "aws_caller_identity" "current" {}
# Outputs
output "cluster_endpoint" {
description = "Endpoint for EKS control plane"
value = aws_eks_cluster.main.endpoint
}
output "cluster_security_group_id" {
description = "Security group ID for the cluster control plane"
value = aws_security_group.cluster.id
}
output "kubectl_config" {
description = "kubectl config as generated by the aws eks get-token command"
value = "aws eks --region ${var.region} update-kubeconfig --name ${aws_eks_cluster.main.name}"
}
output "config_map_aws_auth" {
description = "The aws-auth ConfigMap used to connect to the EKS cluster"
value = kubernetes_config_map.aws_auth.metadata[0].name
}
output "efs_id" {
description = "ID of the EFS file system for persistent storage"
value = aws_efs_file_system.eks.id
}
output "velero_s3_bucket" {
description = "S3 bucket for Velero backups"
value = aws_s3_bucket.velero.id
}
output "private_subnets" {
description = "List of private subnet IDs"
value = aws_subnet.private[*].id
}
output "public_subnets" {
description = "List of public subnet IDs"
value = aws_subnet.public[*].id
}
output "vpc_id" {
description = "VPC ID for the Kubernetes cluster"
value = aws_vpc.main.id
}
output "node_security_group_id" {
description = "Security group ID for the worker nodes"
value = aws_security_group.nodes.id
}
output "cluster_name" {
description = "Kubernetes Cluster Name"
value = aws_eks_cluster.main.name
}
output "cluster_version" {
description = "Kubernetes Cluster Version"
value = aws_eks_cluster.main.version
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment