/terraform-aws-eks-cluster

Terraform module for provisioning an EKS cluster

Primary LanguageHCLApache License 2.0Apache-2.0

terraform-aws-eks-cluster Latest Release Slack Community

README Header

Cloud Posse

Terraform module to provision an EKS cluster on AWS.


This project is part of our comprehensive "SweetOps" approach towards DevOps.

Terraform Open Source Modules

It's 100% Open Source and licensed under the APACHE2.

We literally have hundreds of terraform modules that are Open Source and well-maintained. Check them out!

Introduction

The module provisions the following resources:

  • EKS cluster of master nodes that can be used together with the terraform-aws-eks-workers, terraform-aws-eks-node-group and terraform-aws-eks-fargate-profile modules to create a full-blown cluster
  • IAM Role to allow the cluster to access other AWS services
  • Security Group which is used by EKS workers to connect to the cluster and kubelets and pods to receive communication from the cluster control plane
  • The module creates and automatically applies an authentication ConfigMap to allow the wrokers nodes to join the cluster and to add additional users/roles/accounts

NOTE: The module works with Terraform Cloud.

NOTE: In auth.tf, we added ignore_changes = [data["mapRoles"]] to the kubernetes_config_map for the following reason:

  • We provision the EKS cluster and then the Kubernetes Auth ConfigMap to map additional roles/users/accounts to Kubernetes groups
  • Then we wait for the cluster to become available and for the ConfigMap to get provisioned (see data "null_data_source" "wait_for_cluster_and_kubernetes_configmap" in examples/complete/main.tf)
  • Then we provision a managed Node Group
  • Then EKS updates the Auth ConfigMap and adds worker roles to it (for the worker nodes to join the cluster)
  • Since the ConfigMap is modified outside of Terraform state, Terraform wants to update it (remove the roles that EKS added) on each plan/apply

If you want to modify the Node Group (e.g. add more Node Groups to the cluster) or need to map other IAM roles to Kubernetes groups, set the variable kubernetes_config_map_ignore_role_changes to false and re-provision the module. Then set kubernetes_config_map_ignore_role_changes back to true.

Usage

IMPORTANT: We do not pin modules to versions in our examples because of the difficulty of keeping the versions in the documentation in sync with the latest released versions. We highly recommend that in your code you pin the version to the exact version you are using so that your infrastructure remains stable, and update versions in a systematic way so that they do not catch you by surprise.

Also, because of a bug in the Terraform registry (hashicorp/terraform#21417), the registry shows many of our inputs as required when in fact they are optional. The table below correctly indicates which inputs are required.

For a complete example, see examples/complete.

For automated tests of the complete example using bats and Terratest (which tests and deploys the example on AWS), see test.

Other examples:

  provider "aws" {
    region = var.region
  }

  module "label" {
    source = "cloudposse/label/null"
    # Cloud Posse recommends pinning every module to a specific version
    # version     = "x.x.x"
    namespace  = var.namespace
    name       = var.name
    stage      = var.stage
    delimiter  = var.delimiter
    attributes = compact(concat(var.attributes, list("cluster")))
    tags       = var.tags
  }

  locals {
    # The usage of the specific kubernetes.io/cluster/* resource tags below are required
    # for EKS and Kubernetes to discover and manage networking resources
    # https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#base-vpc-networking
    tags = merge(var.tags, map("kubernetes.io/cluster/${module.label.id}", "shared"))

    # Unfortunately, most_recent (https://github.com/cloudposse/terraform-aws-eks-workers/blob/34a43c25624a6efb3ba5d2770a601d7cb3c0d391/main.tf#L141)
    # variable does not work as expected, if you are not going to use custom AMI you should
    # enforce usage of eks_worker_ami_name_filter variable to set the right kubernetes version for EKS workers,
    # otherwise the first version of Kubernetes supported by AWS (v1.11) for EKS workers will be used, but
    # EKS control plane will use the version specified by kubernetes_version variable.
    eks_worker_ami_name_filter = "amazon-eks-node-${var.kubernetes_version}*"
  }

  module "vpc" {
    source = "cloudposse/vpc/aws"
    # Cloud Posse recommends pinning every module to a specific version
    # version     = "x.x.x"
    namespace  = var.namespace
    stage      = var.stage
    name       = var.name
    attributes = var.attributes
    cidr_block = "172.16.0.0/16"
    tags       = local.tags
  }

  module "subnets" {
    source = "cloudposse/dynamic-subnets/aws"
    # Cloud Posse recommends pinning every module to a specific version
    # version     = "x.x.x"
    availability_zones   = var.availability_zones
    namespace            = var.namespace
    stage                = var.stage
    name                 = var.name
    attributes           = var.attributes
    vpc_id               = module.vpc.vpc_id
    igw_id               = module.vpc.igw_id
    cidr_block           = module.vpc.vpc_cidr_block
    nat_gateway_enabled  = false
    nat_instance_enabled = false
    tags                 = local.tags
  }

  module "eks_workers" {
    source = "cloudposse/eks-workers/aws"
    # Cloud Posse recommends pinning every module to a specific version
    # version     = "x.x.x"
    namespace                          = var.namespace
    stage                              = var.stage
    name                               = var.name
    attributes                         = var.attributes
    tags                               = var.tags
    instance_type                      = var.instance_type
    eks_worker_ami_name_filter          = local.eks_worker_ami_name_filter
    vpc_id                             = module.vpc.vpc_id
    subnet_ids                         = module.subnets.public_subnet_ids
    health_check_type                  = var.health_check_type
    min_size                           = var.min_size
    max_size                           = var.max_size
    wait_for_capacity_timeout          = var.wait_for_capacity_timeout
    cluster_name                       = module.label.id
    cluster_endpoint                   = module.eks_cluster.eks_cluster_endpoint
    cluster_certificate_authority_data = module.eks_cluster.eks_cluster_certificate_authority_data
    cluster_security_group_id          = module.eks_cluster.security_group_id

    # Auto-scaling policies and CloudWatch metric alarms
    autoscaling_policies_enabled           = var.autoscaling_policies_enabled
    cpu_utilization_high_threshold_percent = var.cpu_utilization_high_threshold_percent
    cpu_utilization_low_threshold_percent  = var.cpu_utilization_low_threshold_percent
  }

  module "eks_cluster" {
    source = "cloudposse/eks-cluster/aws"
    # Cloud Posse recommends pinning every module to a specific version
    # version     = "x.x.x"
    namespace  = var.namespace
    stage      = var.stage
    name       = var.name
    attributes = var.attributes
    tags       = var.tags
    vpc_id     = module.vpc.vpc_id
    subnet_ids = module.subnets.public_subnet_ids

    kubernetes_version    = var.kubernetes_version
    oidc_provider_enabled = false

    workers_security_group_ids   = [module.eks_workers.security_group_id]
    workers_role_arns            = [module.eks_workers.workers_role_arn]
  }

Module usage with two worker groups:

  module "eks_workers" {
    source = "cloudposse/eks-workers/aws"
    # Cloud Posse recommends pinning every module to a specific version
    # version     = "x.x.x"
    namespace                          = var.namespace
    stage                              = var.stage
    name                               = "small"
    attributes                         = var.attributes
    tags                               = var.tags
    instance_type                      = "t3.small"
    vpc_id                             = module.vpc.vpc_id
    subnet_ids                         = module.subnets.public_subnet_ids
    health_check_type                  = var.health_check_type
    min_size                           = var.min_size
    max_size                           = var.max_size
    wait_for_capacity_timeout          = var.wait_for_capacity_timeout
    cluster_name                       = module.label.id
    cluster_endpoint                   = module.eks_cluster.eks_cluster_endpoint
    cluster_certificate_authority_data = module.eks_cluster.eks_cluster_certificate_authority_data
    cluster_security_group_id          = module.eks_cluster.security_group_id

    # Auto-scaling policies and CloudWatch metric alarms
    autoscaling_policies_enabled           = var.autoscaling_policies_enabled
    cpu_utilization_high_threshold_percent = var.cpu_utilization_high_threshold_percent
    cpu_utilization_low_threshold_percent  = var.cpu_utilization_low_threshold_percent
  }

  module "eks_workers_2" {
    source = "cloudposse/eks-workers/aws"
    # Cloud Posse recommends pinning every module to a specific version
    # version     = "x.x.x"
    namespace                          = var.namespace
    stage                              = var.stage
    name                               = "medium"
    attributes                         = var.attributes
    tags                               = var.tags
    instance_type                      = "t3.medium"
    vpc_id                             = module.vpc.vpc_id
    subnet_ids                         = module.subnets.public_subnet_ids
    health_check_type                  = var.health_check_type
    min_size                           = var.min_size
    max_size                           = var.max_size
    wait_for_capacity_timeout          = var.wait_for_capacity_timeout
    cluster_name                       = module.label.id
    cluster_endpoint                   = module.eks_cluster.eks_cluster_endpoint
    cluster_certificate_authority_data = module.eks_cluster.eks_cluster_certificate_authority_data
    cluster_security_group_id          = module.eks_cluster.security_group_id

    # Auto-scaling policies and CloudWatch metric alarms
    autoscaling_policies_enabled           = var.autoscaling_policies_enabled
    cpu_utilization_high_threshold_percent = var.cpu_utilization_high_threshold_percent
    cpu_utilization_low_threshold_percent  = var.cpu_utilization_low_threshold_percent
  }

  module "eks_cluster" {
    source = "cloudposse/eks-cluster/aws"
    # Cloud Posse recommends pinning every module to a specific version
    # version     = "x.x.x"
    namespace  = var.namespace
    stage      = var.stage
    name       = var.name
    attributes = var.attributes
    tags       = var.tags
    vpc_id     = module.vpc.vpc_id
    subnet_ids = module.subnets.public_subnet_ids

    kubernetes_version    = var.kubernetes_version
    oidc_provider_enabled = false

    workers_role_arns          = [module.eks_workers.workers_role_arn, module.eks_workers_2.workers_role_arn]
    workers_security_group_ids = [module.eks_workers.security_group_id, module.eks_workers_2.security_group_id]
  }

Makefile Targets

Available targets:

  help                                Help screen
  help/all                            Display help for all targets
  help/short                          This help short screen
  lint                                Lint terraform code

Requirements

Name Version
terraform >= 0.12.26
aws >= 2.0
kubernetes >= 2.0
local >= 1.3
null >= 2.0
template >= 2.0

Providers

Name Version
aws >= 2.0
kubernetes >= 2.0
null >= 2.0

Inputs

Name Description Type Default Required
additional_tag_map Additional tags for appending to tags_as_list_of_maps. Not added to tags. map(string) {} no
allowed_cidr_blocks List of CIDR blocks to be allowed to connect to the EKS cluster list(string) [] no
allowed_security_groups List of Security Group IDs to be allowed to connect to the EKS cluster list(string) [] no
apply_config_map_aws_auth Whether to apply the ConfigMap to allow worker nodes to join the EKS cluster and allow additional users, accounts and roles to acces the cluster bool true no
attributes Additional attributes (e.g. 1) list(string) [] no
cluster_encryption_config_enabled Set to true to enable Cluster Encryption Configuration bool false no
cluster_encryption_config_kms_key_deletion_window_in_days Cluster Encryption Config KMS Key Resource argument - key deletion windows in days post destruction number 10 no
cluster_encryption_config_kms_key_enable_key_rotation Cluster Encryption Config KMS Key Resource argument - enable kms key rotation bool true no
cluster_encryption_config_kms_key_id Specify KMS Key Id ARN to use for cluster encryption config string "" no
cluster_encryption_config_kms_key_policy Cluster Encryption Config KMS Key Resource argument - key policy string null no
cluster_encryption_config_resources Cluster Encryption Config Resources to encrypt, e.g. ['secrets'] list(any)
[
"secrets"
]
no
cluster_log_retention_period Number of days to retain cluster logs. Requires enabled_cluster_log_types to be set. See https://docs.aws.amazon.com/en_us/eks/latest/userguide/control-plane-logs.html. number 0 no
context Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as null to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional_tag_map, which are merged.
object({
enabled = bool
namespace = string
environment = string
stage = string
name = string
delimiter = string
attributes = list(string)
tags = map(string)
additional_tag_map = map(string)
regex_replace_chars = string
label_order = list(string)
id_length_limit = number
})
{
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_order": [],
"name": null,
"namespace": null,
"regex_replace_chars": null,
"stage": null,
"tags": {}
}
no
delimiter Delimiter to be used between namespace, environment, stage, name and attributes.
Defaults to - (hyphen). Set to "" to use no delimiter at all.
string null no
enabled Set to false to prevent the module from creating any resources bool null no
enabled_cluster_log_types A list of the desired control plane logging to enable. For more information, see https://docs.aws.amazon.com/en_us/eks/latest/userguide/control-plane-logs.html. Possible values [api, audit, authenticator, controllerManager, scheduler] list(string) [] no
endpoint_private_access Indicates whether or not the Amazon EKS private API server endpoint is enabled. Default to AWS EKS resource and it is false bool false no
endpoint_public_access Indicates whether or not the Amazon EKS public API server endpoint is enabled. Default to AWS EKS resource and it is true bool true no
environment Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT' string null no
id_length_limit Limit id to this many characters.
Set to 0 for unlimited length.
Set to null for default, which is 0.
Does not affect id_full.
number null no
kubernetes_config_map_ignore_role_changes Set to true to ignore IAM role changes in the Kubernetes Auth ConfigMap bool true no
kubernetes_version Desired Kubernetes master version. If you do not specify a value, the latest available version is used string "1.15" no
label_order The naming order of the id output and Name tag.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 5 elements, but at least one must be present.
list(string) null no
local_exec_interpreter shell to use for local_exec list(string)
[
"/bin/sh",
"-c"
]
no
map_additional_aws_accounts Additional AWS account numbers to add to config-map-aws-auth ConfigMap list(string) [] no
map_additional_iam_roles Additional IAM roles to add to config-map-aws-auth ConfigMap
list(object({
rolearn = string
username = string
groups = list(string)
}))
[] no
map_additional_iam_users Additional IAM users to add to config-map-aws-auth ConfigMap
list(object({
userarn = string
username = string
groups = list(string)
}))
[] no
name Solution name, e.g. 'app' or 'jenkins' string null no
namespace Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' string null no
oidc_provider_enabled Create an IAM OIDC identity provider for the cluster, then you can create IAM roles to associate with a service account in the cluster, instead of using kiam or kube2iam. For more information, see https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html bool false no
public_access_cidrs Indicates which CIDR blocks can access the Amazon EKS public API server endpoint when enabled. EKS defaults this to a list with 0.0.0.0/0. list(string)
[
"0.0.0.0/0"
]
no
regex_replace_chars Regex to replace chars with empty string in namespace, environment, stage and name.
If not set, "/[^a-zA-Z0-9-]/" is used to remove all characters other than hyphens, letters and digits.
string null no
region AWS Region string n/a yes
stage Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release' string null no
subnet_ids A list of subnet IDs to launch the cluster in list(string) n/a yes
tags Additional tags (e.g. map('BusinessUnit','XYZ') map(string) {} no
vpc_id VPC ID for the EKS cluster string n/a yes
wait_for_cluster_command local-exec command to execute to determine if the EKS cluster is healthy. Cluster endpoint are available as environment variable ENDPOINT string "curl --silent --fail --retry 60 --retry-delay 5 --retry-connrefused --insecure --output /dev/null $ENDPOINT/healthz" no
workers_role_arns List of Role ARNs of the worker nodes list(string) [] no
workers_security_group_ids Security Group IDs of the worker nodes list(string) [] no

Outputs

Name Description
cluster_encryption_config_enabled If true, Cluster Encryption Configuration is enabled
cluster_encryption_config_provider_key_alias Cluster Encryption Config KMS Key Alias ARN
cluster_encryption_config_provider_key_arn Cluster Encryption Config KMS Key ARN
cluster_encryption_config_resources Cluster Encryption Config Resources
eks_cluster_arn The Amazon Resource Name (ARN) of the cluster
eks_cluster_certificate_authority_data The Kubernetes cluster certificate authority data
eks_cluster_endpoint The endpoint for the Kubernetes API server
eks_cluster_id The name of the cluster
eks_cluster_identity_oidc_issuer The OIDC Identity issuer for the cluster
eks_cluster_identity_oidc_issuer_arn The OIDC Identity issuer ARN for the cluster that can be used to associate IAM roles with a service account
eks_cluster_managed_security_group_id Security Group ID that was created by EKS for the cluster. EKS creates a Security Group and applies it to ENI that is attached to EKS Control Plane master nodes and to any managed workloads
eks_cluster_role_arn ARN of the EKS cluster IAM role
eks_cluster_version The Kubernetes server version of the cluster
kubernetes_config_map_id ID of aws-auth Kubernetes ConfigMap
security_group_arn ARN of the EKS cluster Security Group
security_group_id ID of the EKS cluster Security Group
security_group_name Name of the EKS cluster Security Group

Share the Love

Like this project? Please give it a ★ on our GitHub! (it helps us a lot)

Are you using this project or any of our other projects? Consider leaving a testimonial. =)

Related Projects

Check out these related projects.

Help

Got a question? We got answers.

File a GitHub issue, send us an email or join our Slack Community.

README Commercial Support

DevOps Accelerator for Startups

We are a DevOps Accelerator. We'll help you build your cloud infrastructure from the ground up so you can own it. Then we'll show you how to operate it and stick around for as long as you need us.

Learn More

Work directly with our team of DevOps experts via email, slack, and video conferencing.

We deliver 10x the value for a fraction of the cost of a full-time engineer. Our track record is not even funny. If you want things done right and you need it done FAST, then we're your best bet.

  • Reference Architecture. You'll get everything you need from the ground up built using 100% infrastructure as code.
  • Release Engineering. You'll have end-to-end CI/CD with unlimited staging environments.
  • Site Reliability Engineering. You'll have total visibility into your apps and microservices.
  • Security Baseline. You'll have built-in governance with accountability and audit logs for all changes.
  • GitOps. You'll be able to operate your infrastructure via Pull Requests.
  • Training. You'll receive hands-on training so your team can operate what we build.
  • Questions. You'll have a direct line of communication between our teams via a Shared Slack channel.
  • Troubleshooting. You'll get help to triage when things aren't working.
  • Code Reviews. You'll receive constructive feedback on Pull Requests.
  • Bug Fixes. We'll rapidly work with you to fix any bugs in our projects.

Slack Community

Join our Open Source Community on Slack. It's FREE for everyone! Our "SweetOps" community is where you get to talk with others who share a similar vision for how to rollout and manage infrastructure. This is the best place to talk shop, ask questions, solicit feedback, and work together as a community to build totally sweet infrastructure.

Discourse Forums

Participate in our Discourse Forums. Here you'll find answers to commonly asked questions. Most questions will be related to the enormous number of projects we support on our GitHub. Come here to collaborate on answers, find solutions, and get ideas about the products and services we value. It only takes a minute to get started! Just sign in with SSO using your GitHub account.

Newsletter

Sign up for our newsletter that covers everything on our technology radar. Receive updates on what we're up to on GitHub as well as awesome new projects we discover.

Office Hours

Join us every Wednesday via Zoom for our weekly "Lunch & Learn" sessions. It's FREE for everyone!

zoom

Contributing

Bug Reports & Feature Requests

Please use the issue tracker to report any bugs or file feature requests.

Developing

If you are interested in being a contributor and want to get involved in developing this project or help out with our other projects, we would love to hear from you! Shoot us an email.

In general, PRs are welcome. We follow the typical "fork-and-pull" Git workflow.

  1. Fork the repo on GitHub
  2. Clone the project to your own machine
  3. Commit changes to your own branch
  4. Push your work back up to your fork
  5. Submit a Pull Request so that we can review your changes

NOTE: Be sure to merge the latest changes from "upstream" before making a pull request!

Copyright

Copyright © 2017-2021 Cloud Posse, LLC

License

License

See LICENSE for full details.

Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.

Trademarks

All other trademarks referenced herein are the property of their respective owners.

About

This project is maintained and funded by Cloud Posse, LLC. Like it? Please let us know by leaving a testimonial!

Cloud Posse

We're a DevOps Professional Services company based in Los Angeles, CA. We ❤️ Open Source Software.

We offer paid support on all of our projects.

Check out our other projects, follow us on twitter, apply for a job, or hire us to help with your cloud strategy and implementation.

Contributors

Erik Osterman
Erik Osterman
Andriy Knysh
Andriy Knysh
Igor Rodionov
Igor Rodionov
Oscar
Oscar

README Footer Beacon