Elastic Kubernetes Service Module

This module will build and configure an EKS cluster with additional node pools

This repository is a READ-ONLY sub-tree split. See https://github.com/FriendsOfTerraform/modules to create issues or submit pull requests.

Table of Contents

Example Usage

Basic Usage

This example creates an EKS cluster with a one node primary node pool. The public API endpoint for the cluster is disabled by default, we will still be assigning multiple public subnets where the load balancers will be deployed, however.

module "demo_eks_cluster" {
  source = "github.com/FriendsOfTerraform/aws-eks.git?ref=v1.1.0"

  name = "demo-eks"

  vpc_config = {
    subnet_ids = [
      "subnet-029fd1fxxxxxxxx", # public-us-east-1a
      "subnet-02d1ba8xxxxxxxx", # public-us-east-1b
      "subnet-0f8c5afxxxxxxxx", # public-us-east-1c
      "subnet-0e08038xxxxxxxx", # private-us-east-1a
      "subnet-09b6fc5xxxxxxxx", # private-us-east-1b
      "subnet-0c7b976xxxxxxxx"  # private-us-east-1c
    ]
  }

  node_groups = {
    # Manages multiple node groups
    # The key of the map will be the node group's name
    linux = {
      desired_instances = 1

      # worker nodes should only be deployed in private subnets
      subnet_ids = [
        "subnet-0e08038xxxxxxxx", # private-us-east-1a
        "subnet-09b6fc5xxxxxxxx", # private-us-east-1b
        "subnet-0c7b976xxxxxxxx"  # private-us-east-1c
      ]
    }
    windows = {
      desired_instances = 1
      ami_type          = "WINDOWS_CORE_2022_x86_64"

      # worker nodes should only be deployed in private subnets
      subnet_ids = [
        "subnet-0e08038xxxxxxxx", # private-us-east-1a
        "subnet-09b6fc5xxxxxxxx", # private-us-east-1b
        "subnet-0c7b976xxxxxxxx"  # private-us-east-1c
      ]
    }
  }
}

IAM Roles For Service Accounts

This example demonstrates how to enable IAM roles for services account, which allows you to associate an IAM role to a Kubernetes service account. Please refer to this documentation to learn how to establish the association from the Kubernetes end.

module "demo_eks_irsa" {
  source = "github.com/FriendsOfTerraform/aws-eks.git?ref=v1.1.0"

  name = "demo-eks-irsa"

  vpc_config = {
    subnet_ids = [
      "subnet-029fd1fxxxxxxxx", # public-us-east-1a
      "subnet-02d1ba8xxxxxxxx", # public-us-east-1b
      "subnet-0e08038xxxxxxxx", # private-us-east-1a
      "subnet-09b6fc5xxxxxxxx", # private-us-east-1b
    ]
  }

  service_account_to_iam_role_mappings = {
    # The key of the map specifies the Kubernetes <namespace>/<service account> to create an IAM role for
    # You can specify only the namespace name in the key to allow the IAM role to be used by all service accounts in the namespace
    # The IAM role will be attached to the list of IAM policies specified

    # Associate every service account in the `default` namespace to AmazonS3FullAccess policy
    default = ["arn:aws:iam::aws:policy/AmazonS3FullAccess"]

    # Associate the `my-sa` service account in the `my-ns` namespace to two policies
    "my-ns/my-sa" = [
      "arn:aws:iam::aws:policy/AmazonS3FullAccess",
      "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
    ]
  }
}

OIDC Identity Provider

This example configures an external OIDC identity provider for authenticating to the Kubernetes API server.

module "demo_eks_oidc" {
  source = "github.com/FriendsOfTerraform/aws-eks.git?ref=v1.1.0"

  name = "demo-eks-oidc"

  vpc_config = {
    subnet_ids = [
      "subnet-029fd1fxxxxxxxx", # public-us-east-1a
      "subnet-02d1ba8xxxxxxxx", # public-us-east-1b
      "subnet-0e08038xxxxxxxx", # private-us-east-1a
      "subnet-09b6fc5xxxxxxxx", # private-us-east-1b
    ]
  }

  eks_oidc_identity_provider = {
    client_id      = "65a083ca-4398-450e-8cc2-af19cee7a423"
    groups_claim   = "gid:_groups"
    issuer_url     = "https://login.microsoftonline.com/8d6fb1c6-f181-4af2-928e-1c1bd4d56b5e/v2.0"
    name           = "azure-ad"
    username_claim = "email"
  }
}

Add-Ons

This example demonstrates how to manage multiple EKS add-ons. Some add-on requires additional permissions granted using service account to IAM role, those are automatically created and configured for add-ons from AWS.

module "demo_eks_addon" {
  source = "github.com/FriendsOfTerraform/aws-eks.git?ref=v1.1.0"

  name = "demo-eks-addon"

  vpc_config = {
    subnet_ids = [
      "subnet-029fd1fxxxxxxxx", # public-us-east-1a
      "subnet-02d1ba8xxxxxxxx", # public-us-east-1b
      "subnet-0e08038xxxxxxxx", # private-us-east-1a
      "subnet-09b6fc5xxxxxxxx", # private-us-east-1b
    ]
  }

  add_ons = {
    aws-ebs-csi-driver = { version = "v1.19.0-eksbuild.2" }
    coredns            = { version = "v1.10.1-eksbuild.1" }
    kube-proxy         = { version = "v1.27.1-eksbuild.1" }
    vpc-cni            = { version = "v1.12.6-eksbuild.2" }
  }
}

Argument Reference

Mandatory

  • (string) name [since v1.0.0]

    The name of the EKS cluster. All associated resources will also have their name prefixed with this value

  • (map(object)) node_groups [since v1.0.0]

    Map of worker node groups, see example

    • (number) desired_instances [since v1.0.0]

      Number of desired worker nodes

    • (list(string)) subnet_ids [since v1.0.0]

      List of subnet IDs where the nodes will be deployed on. In addition, you must ensure that the subnets are tagged with the following values in order for a load balancer service to deployed successfully. Please refer to the VPC and subnet requirements for more information

      Subnet type Tag
      public kubernetes.io/role/elb = 1
      private kubernetes.io/role/internal-elb = 1
    • (map(string)) additional_tags = {} [since v1.0.0]

      Additional tags for the node group

    • (string) ami_type = "AL2_x86_64" [since v1.0.0]

      Type of Amazon Machine Image (AMI) associated with the EKS Node Group. Please refer to this documentation for valid values

    • (string) ami_release_version = null [since v1.0.0]

      AMI version of the EKS Node Group. Defaults to latest version for Kubernetes version. Please refer to this link for a list of the latest release versions.

    • (string) capacity_type = "ON_DEMAND" [since v1.0.0]

      Type of capacity associated with the EKS Node Group. Valid values are ON_DEMAND, SPOT

    • (number) disk_size = 20 [since v1.0.0]

      EBS size for the worker nodes in GB

    • (bool) ignores_pod_disruption_budget = false [since v1.0.0]

      Force version update if existing pods are unable to be drained due to a pod disruption budget issue

    • (string) instance_type = "t3.medium" [since v1.0.0]

      EC2 instance type for the worker nodes

    • (map(string)) kubernetes_labels = {} [since v1.0.0]

      Map of Kubernetes labels for the nodes

    • (map(string)) kubernetes_taints = {} [since v1.0.0]

      Map of Kubernetes taints for the nodes. In the following format: {key = value:effect}. Valid effects are: NO_EXECUTE, NO_SCHEDULE, PREFER_NO_SCHEDULE. For example

      kubernetes_taints = {foo = "bar:NO_EXECUTE"}
      
    • (string) kubernetes_version = null [since v1.0.0]

      Desired Kubernetes worker version. Refer to this page for a list of supported versions. Defaults to latest version if null.

    • (number) max_instances = null [since v1.0.0]

      Number of maximum worker nodes this group can scale to. Defaults to desired_instances if unspecifed

    • (string) max_unavailable_instances_during_update = 1 [since v1.0.0]

      Desired max number of unavailable worker nodes during node group update. This can be a whole number ("2"), or a percentage ("50%")

    • (number) min_instances = null [since v1.0.0]

      Number of minimum worker nodes this group can scale to. Defaults to desired_instances if unspecifed

  • (object) vpc_config [since v1.0.0]

    VPC configuration for the EKS cluster

    • (list(string)) subnet_ids [since v1.0.0]

      List of subnet IDs. Must be in at least two different availability zones. Amazon EKS creates cross-account elastic network interfaces in these subnets to allow communication between your worker nodes and the Kubernetes control plane. Please refer to the VPC and subnet requirements to make sure your VPC meets the requirement

    • (list(string)) security_group_ids = [] [since v1.0.0]

      List of security group IDs for the cross-account elastic network interfaces that Amazon EKS creates to use to allow communication between your worker nodes and the Kubernetes control plane.

Optional

  • (map(string)) additional_tags = {} [since v1.0.0]

    Additional tags for the Kubernetes cluster

  • (map(string)) additional_tags_all = {} [since v1.0.0]

    Additional tags for all resources deployed with this module

  • (map(object)) add_ons = {} [since v1.0.0]

    Configures multiple EKS add-ons, see example. You can get a list of add-on names by running this aws cli command:

    aws eks describe-addon-versions | jq -r ".addons[] | .addonName"
    • (map(string)) additional_tags = {} [since v1.0.0]

      Additional tags for the add-on

    • (string) configuration = null [since v1.0.0]

      Custom configuration values for add-ons with single JSON string. You can use the describe-addon-configuration call to find the correct JSON schema for each add-on. For example:

      aws eks describe-addon-configuration --addon-name vpc-cni --addon-version v1.12.6-eksbuild.2
    • (string) iam_role_arn = null [since v1.0.0]

      The arn of an existing IAM role to bind to the add-on's service account. The role must be assigned the IAM permissions required by the add-on. If you don't specify an existing IAM role, an IAM role will be created automatically for supported add-ons (see below), otherwise the add-on uses the permissions assigned to the node IAM role.

      supported add-ons: vpc-cni, aws-ebs-csi-driver, and adot

    • (bool) preserve = false [since v1.0.0]

      Indicates if you want to preserve the created resources when deleting the EKS add-on

    • (string) resolve_conflicts_on_create = "NONE" [since v1.0.0]

      How to resolve field value conflicts when migrating a self-managed add-on to an Amazon EKS add-on. Valid values are "NONE" and "OVERWRITE"

    • (string) resolve_conflicts_on_update = "NONE" [since v1.0.0]

      How to resolve field value conflicts for an Amazon EKS add-on if you've changed a value from the Amazon EKS default value. Valid values are "NONE" and "OVERWRITE"

    • (string) version = null [since v1.0.0]

      The version of the EKS add-on. Defaults to the latest version if null. You can get a list of add-on and their latest version with this command:

      aws eks describe-addon-versions --kubernetes-version 1.27 | jq -r ".addons[] | .addonName, .addonVersions[0].addonVersion"
  • (list(string)) apiserver_allowed_cidrs = ["0.0.0.0/0"] [since v1.0.0]

    List of CIDR to allow access to the Kubernetes API endpoint

  • (bool) enable_apiserver_public_endpoint = false [since v1.0.0]

    Enables the EKS public endpoint. Cluster internal traffic will still be private.

  • (list(string)) enable_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"] [since v1.0.0]

    List of the desired control plane logging to enable. Refer to this link for valid values.

  • (object) envelope_encryption = null [since v1.0.0]

    Configures envelope encryption for Kubernetes secrets

    • (string) kms_key_arn [since v1.0.0]

      ARN of the Key Management Service (KMS) customer master key (CMK) for encryption. The CMK must be symmetric, created in the same region as the cluster, and if the CMK was created in a different account, the user must have access to the CMK.

  • (object) kubernetes_networking_config = null [since v1.0.0]

    Configures various Kubernetes networking options

    • (string) kubernetes_service_address_range = null [since v1.0.0]

      The CIDR block to assign Kubernetes pod and service IP addresses from. If you don't specify a block, Kubernetes assigns addresses from either the 10.100.0.0/16 or 172.20.0.0/16 CIDR blocks. The block must meet the following requirements:

      • Within one of the following private IP address blocks: "10.0.0.0/8", "172.16.0.0/12", or "192.168.0.0/16"
      • Doesn't overlap with any CIDR block assigned to the VPC that you selected for VPC
      • Between /24 and /12
    • (string) ip_family = "ipv4" [since v1.0.0]

      The IP family used to assign Kubernetes pod and service addresses. Valid values are "ipv4", "ipv6"

  • (string) kubernetes_version = null [since v1.0.0]

    Desired Kubernetes master version. Refer to this page for a list of supported versions

  • (object) oidc_identity_provider = null [since v1.0.0]

    Set up an EKS OIDC identity provider, see example

    • (string) client_id [since v1.0.0]

      Client ID for the OIDC provider

    • (string) groups_claim [since v1.0.0]

      The JWT claim that the provider will use to return groups. This is mapped to a Kubernetes group. You can optionally prepend a prefix to this claim by separating the prefix with a _. eg gid:_groups

    • (string) issuer_url [since v1.0.0]

      Issuer URL for the OIDC identity provider. This URL should point to the level below .well-known/openid-configuration and must be publicly accessible over the internet.

    • (string) name [since v1.0.0]

      A friendly name for this identity provider

    • (string) username_claim [since v1.0.0]

      The JWT claim that the provider will use as the username. This is mapped to a Kubernetes user. You can optionally prepend a prefix to this claim by separating the prefix with a _. eg uid:_email

  • (map(list(string))) service_account_to_iam_role_mappings = {} [since v1.0.0]

    Enables and creates the components needed for IAM roles for service accounts, then map a Kubernetes Namespace/ServiceAccount to a list of IAM policies. You can map the entire namespace to a role by omitting <service_account>, please see example

Outputs

  • (string) cluster_arn [since v1.0.0]

    The ARN of the EKS cluster

  • (string) cluster_certificate_authority [since v1.0.0]

    The public CA certificate (based64) of the EKS cluster

  • (string) cluster_endpoint_url [since v1.0.0]

    The endpoint URL of the EKS cluster

  • (string) cluster_name [since v1.1.0]

    The name of the EKS cluster

  • (string) cluster_role_arn [since v1.1.0]

    The ARN of the cluster IAM role

  • (map(string)) node_group_arns [since v1.0.0]

    Map of ARNs of all the node groups associated to this cluster

  • (string) node_role_arn [since v1.1.0]

    The ARN of the node IAM role

  • (string) aws_cli_connect_to_cluster_command [since v1.0.0]

    The AWS cli command to connect to the EKS cluster

Known Limitations

Editing Node Group Configuration

Because the EKS node group is deployed using EC2 auto scaling group, updating node groups' configuration after creation will result in the creation of a new auto scaling group, effectively replacing the entire node group. However, node group replacement follows the Kubernetes node termination procedure, where all workloads will be automatically moved to the next healthy node group if available.