provider "aws" { region = local.region } provider "aws" { region = "us-east-1" alias = "virginia" } provider "helm" { kubernetes { host = module.eks.cluster_endpoint cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) exec { api_version = "client.authentication.k8s.io/v1beta1" command = "aws" # This requires the awscli to be installed locally where Terraform is executed args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] } } } provider "kubectl" { apply_retry_count = 5 host = module.eks.cluster_endpoint cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) load_config_file = false exec { api_version = "client.authentication.k8s.io/v1beta1" command = "aws" # This requires the awscli to be installed locally where Terraform is executed args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] } } data "aws_availability_zones" "available" { # Exclude local zones filter { name = "opt-in-status" values = ["opt-in-not-required"] } } data "aws_ecrpublic_authorization_token" "token" { provider = aws.virginia } locals { name = "ex-${basename(path.cwd)}" region = "eu-west-1" vpc_cidr = "10.0.0.0/16" azs = slice(data.aws_availability_zones.available.names, 0, 3) tags = { Example = local.name GithubRepo = "terraform-aws-eks" GithubOrg = "terraform-aws-modules" } } ################################################################################ # EKS Module ################################################################################ module "eks" { source = "../.." cluster_name = local.name cluster_version = "1.31" # Gives Terraform identity admin access to cluster which will # allow deploying resources (Karpenter) into the cluster enable_cluster_creator_admin_permissions = true cluster_endpoint_public_access = true cluster_addons = { coredns = {} eks-pod-identity-agent = {} kube-proxy = {} vpc-cni = {} } vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.private_subnets control_plane_subnet_ids = module.vpc.intra_subnets eks_managed_node_groups = { karpenter = { ami_type = "AL2023_x86_64_STANDARD" instance_types = ["m5.large"] min_size = 2 max_size = 3 desired_size = 2 } } # cluster_tags = merge(local.tags, { # NOTE - only use this option if you are using "attach_cluster_primary_security_group" # and you know what you're doing. In this case, you can remove the "node_security_group_tags" below. # "karpenter.sh/discovery" = local.name # }) node_security_group_tags = merge(local.tags, { # NOTE - if creating multiple security groups with this module, only tag the # security group that Karpenter should utilize with the following tag # (i.e. - at most, only one security group should have this tag in your account) "karpenter.sh/discovery" = local.name }) tags = local.tags } ################################################################################ # Karpenter ################################################################################ module "karpenter" { source = "../../modules/karpenter" cluster_name = module.eks.cluster_name enable_v1_permissions = true enable_pod_identity = true create_pod_identity_association = true # Used to attach additional IAM policies to the Karpenter node IAM role node_iam_role_additional_policies = { AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" } tags = local.tags } module "karpenter_disabled" { source = "../../modules/karpenter" create = false } ################################################################################ # Karpenter Helm chart & manifests # Not required; just to demonstrate functionality of the sub-module ################################################################################ resource "helm_release" "karpenter" { namespace = "kube-system" name = "karpenter" repository = "oci://public.ecr.aws/karpenter" repository_username = data.aws_ecrpublic_authorization_token.token.user_name repository_password = data.aws_ecrpublic_authorization_token.token.password chart = "karpenter" version = "1.1.0" wait = false values = [ <<-EOT dnsPolicy: Default settings: clusterName: ${module.eks.cluster_name} clusterEndpoint: ${module.eks.cluster_endpoint} interruptionQueue: ${module.karpenter.queue_name} webhook: enabled: false EOT ] } resource "kubectl_manifest" "karpenter_node_class" { yaml_body = <<-YAML apiVersion: karpenter.k8s.aws/v1beta1 kind: EC2NodeClass metadata: name: default spec: amiFamily: AL2023 role: ${module.karpenter.node_iam_role_name} subnetSelectorTerms: - tags: karpenter.sh/discovery: ${module.eks.cluster_name} securityGroupSelectorTerms: - tags: karpenter.sh/discovery: ${module.eks.cluster_name} tags: karpenter.sh/discovery: ${module.eks.cluster_name} YAML depends_on = [ helm_release.karpenter ] } resource "kubectl_manifest" "karpenter_node_pool" { yaml_body = <<-YAML apiVersion: karpenter.sh/v1beta1 kind: NodePool metadata: name: default spec: template: spec: nodeClassRef: name: default requirements: - key: "karpenter.k8s.aws/instance-category" operator: In values: ["c", "m", "r"] - key: "karpenter.k8s.aws/instance-cpu" operator: In values: ["4", "8", "16", "32"] - key: "karpenter.k8s.aws/instance-hypervisor" operator: In values: ["nitro"] - key: "karpenter.k8s.aws/instance-generation" operator: Gt values: ["5"] limits: cpu: 1000 disruption: consolidationPolicy: WhenEmpty consolidateAfter: 30s YAML depends_on = [ kubectl_manifest.karpenter_node_class ] } # Example deployment using the [pause image](https://www.ianlewis.org/en/almighty-pause-container) # and starts with zero replicas resource "kubectl_manifest" "karpenter_example_deployment" { yaml_body = <<-YAML apiVersion: apps/v1 kind: Deployment metadata: name: inflate spec: replicas: 0 selector: matchLabels: app: inflate template: metadata: labels: app: inflate spec: terminationGracePeriodSeconds: 0 containers: - name: inflate image: public.ecr.aws/eks-distro/kubernetes/pause:3.7 resources: requests: cpu: 1 YAML depends_on = [ helm_release.karpenter ] } ################################################################################ # Supporting Resources ################################################################################ module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 5.0" name = local.name cidr = local.vpc_cidr azs = local.azs private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] intra_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 52)] enable_nat_gateway = true single_nat_gateway = true public_subnet_tags = { "kubernetes.io/role/elb" = 1 } private_subnet_tags = { "kubernetes.io/role/internal-elb" = 1 # Tags subnets for Karpenter auto-discovery "karpenter.sh/discovery" = local.name } tags = local.tags }