# terraform-aws-eks-cluster: the control plane half of a Cloud Posse EKS setup

> This Cloud Posse module provisions the EKS control plane, its IAM role and its access entries, and leaves the worker nodes to a separate module. The interesting part is the documented preference for a static access entry map over granting the caller admin, which is the opposite of what the example does.

**cloudposse/terraform-aws-eks-cluster** — Terraform module for provisioning an EKS cluster

- Repository: https://github.com/cloudposse/terraform-aws-eks-cluster
- Website: https://cloudposse.com/accelerate
- Stars: 552 · Forks: 368
- Language: HCL
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/cloudposse-terraform-aws-eks-cluster

## A control plane module, not a cluster module

This Terraform module provisions a fully configured AWS EKS, Elastic Kubernetes Service, cluster, and it is engineered to integrate smoothly with Karpenter and EKS addons, forming a critical part of Cloud Posse's reference architecture.

The Introduction section then narrows the scope by listing what the module actually provisions, and the split is the first thing to understand.

It provisions an EKS cluster of master nodes that can be used together with the terraform-aws-eks-node-group and terraform-aws-eks-fargate-profile modules to create a full-blown EKS/Kubernetes cluster. You can also use the terraform-aws-eks-workers module to provision worker nodes for the cluster, but the README is direct that it is now rare for that to be a better choice than to use terraform-aws-eks-node-group.

The other two resources are an IAM Role to allow the cluster to access other AWS services, and EKS access entries to allow IAM users to access and administer the cluster.

So the module is the control plane, the networking and the IAM, and the compute is somebody else's problem. That is the standard modern shape, where node provisioning is handled by a cluster autoscaler like Karpenter rather than by instance groups in the control plane module, and it is worth understanding before you evaluate it, because the module looks smaller than the name suggests.

The module is Apache-2.0 licensed, the primary language is HCL, the default branch is main, and the release history is steady: v4.15.0 on 2026-09-15, v4.14.0 on 2026-09-08 and v4.13.0 on 2026-08-18, with the last push on 2026-10-20.

## The example grants admin, and tells you not to copy that

The usage example is a HCL file, and the comments in it are unusually instructive, because they point out where the example is deliberately not production shaped.

The first comment covers the caller identity and the session context. The example creates an explicit access entry for the current user, but in practice you should use a static map of IAM users or roles that should have access to the cluster, and granting access to the current user in this way is not recommended for production use. The IAM session context exists to convert an assumed role ARN into an IAM Role ARN, and the comment says again that this is primarily to simplify the example.

Then the access entry map itself, which is where the module's main access control feature lives:

```hcl
    access_entry_map = {
      (data.aws_iam_session_context.current.issuer_arn) = {
        access_policy_associations = {
          ClusterAdmin = {}
        }
      }
    }
```

The comment above it explains the intent: enable the IAM user creating the cluster to administer it, without using the bootstrap_cluster_creator_admin_permissions option, as an example of how to use the access_entry_map feature. In practice this should be replaced with a static map of IAM users or roles that should have access to the cluster, but the current user is used to simplify the example.

So the module has two routes to administrator access. The convenience route, bootstrap_cluster_creator_admin_permissions, grants the creator automatically. The explicit route is the access_entry_map, where you name the principals and the policies they get. The example demonstrates the second and recommends it, while making the first available.

That combination, a feature that lets you avoid the implicit grant and an example that shows you how, is the kind of thing that separates a module written by people who have operated EKS from one written to pass a first tutorial.

## The networking tags in the example are not optional

The locals block in the example is where the Kubernetes specific requirements of EKS show up, and every line of it has a reason attached.

The cluster tag is the important one. The comment says the usage of the specific kubernetes.io/cluster/* resource tags below are required for EKS and Kubernetes to discover and manage networking resources, with a link to the AWS knowledge centre article on EKS VPC subnet discovery and a second link to the aws-load-balancer-controller documentation on subnet discovery. The value is derived from the module label, so it is a shared tag across the resources that belong to the cluster.

Then the two public and private subnet tag sets, and these have a second purpose beyond discovery. The comment says they are required to make ALB ingress work, with a link to the EKS user guide page for ALB ingress. The public subnets get the kubernetes.io/role/elb tag and the private subnets get kubernetes.io/role/internal-elb.

So an EKS cluster in a VPC has two sets of requirements pulling on the same subnet tags: the cluster discovery tags, and the load balancer role tags. The module needs all three, and a hand-built VPC will not have them unless somebody adds them.

The example composes three other Cloud Posse modules to produce that VPC. There is a label module from cloudposse/label/null that takes the namespace, name, stage, delimiter and tags, with a comment noting that Cloud Posse recommends pinning every module to a specific version. There is a vpc module from cloudposse/vpc/aws with an ipv4_primary_cidr_block of 172.16.0.0/16, and a subnets module from cloudposse/dynamic-subnets/aws that takes the availability zones, the VPC id, the internet gateway id, the CIDR block and a nat_gateway_enabled flag set to true.

## Atmos is how Cloud Posse expects you to run it

The README has a tip block recommending Atmos with Terraform, and it explains why the module is documented the way it is.

Cloud Posse uses atmos to easily orchestrate multiple environments using Terraform, and it works with Github Actions, Atlantis, or Spacelift. So the environment layer sits above Terraform, in a tool that keeps stacks, components and environments in one place, and the module is designed to be called from that rather than from a hand-written Terraform workspace.

The README file itself is generated, and the comment at the top of it says so in detail. It says not to edit the file, that it was automatically generated by the cloudposse/build-harness, and that you should make all changes to README.yaml, install atmos, and run atmos readme to rebuild the file. The closing line is a candid admission about why the process is automated at all: they maintain hundreds of open source projects, and this is how they maintain their sanity.

The repository layout is consistent with that. There is a README.yaml and a README.md, an atmos.yaml, and the Terraform files split by concern at the root: auth.tf, capabilities.tf, context.tf, iam.tf, main.tf, outputs.tf, security-group.tf, variables.tf and versions.tf. There is a docs/ directory, an examples/ directory with a complete example and an obsolete-version2 example, and a test/ directory.

The homepage field points at cloudposse.com/accelerate, which is the commercial support offering, so the split between the open module and the paid support is explicit in the metadata.

## The example is tested, which is unusual for a module

The usage section points at the test setup, and it is worth reading because most Terraform modules do not have one.

For a complete example, see examples/complete. For automated tests of the complete example, the README points at bats and Terratest, noting that Terratest tests and deploys the example on AWS, with the tests under test/src.

That distinction matters. A bats test checks the Terraform files, the plan output, or the rendered values. A Terratest test provisions the actual infrastructure in a real AWS account and then asserts on it. The second kind is slow and costs money and it is the only kind that tells you whether the cluster really comes up, whether the access entries work and whether the subnet tags are right.

There is a second pointer to the service catalog, at terraform-aws-components/eks/cluster, which is Cloud Posse's collection of root module invocations for provisioning reference architectures. The difference between the two is worth understanding. A root module invocation is a wired-together set of modules with a particular set of inputs, and the catalog is a list of those. So the question of whether the cluster module is right for you is really two questions: whether the module does what you need, and which of the catalog entries in the service catalog is the wiring you want.

One thing to weigh is that this module assumes Cloud Posse conventions, so adopting it means adopting the label module, the context pattern and the Atmos layer along with it.

## Conclusion

Adopt terraform-aws-eks-cluster if you want an EKS control plane that fits Cloud Posse's module family, with access entries, Karpenter compatibility and EKS addon wiring already handled, and you are willing to take the whole set of module conventions rather than one vendor's defaults. Do not adopt it for the worker nodes, because that is a separate module and the README says using the workers module is now rarely the better choice. Before you copy the example, read the access entry section, because the example grants the current user cluster admin to keep it simple and says explicitly that you should replace it with a static map of IAM users or roles.

## FAQ

### What does the terraform-aws-eks-cluster module provision?

An EKS cluster of master nodes that can be combined with the terraform-aws-eks-node-group and terraform-aws-eks-fargate-profile modules, an IAM Role allowing the cluster to access other AWS services, and EKS access entries that let IAM users access and administer the cluster.

### How should I configure EKS access entries in this module?

Use the access_entry_map with a static map of IAM users or roles and the access policy associations you want, as the example shows. The README notes that granting the current user access this way, which is what the example does to keep it simple, is not recommended for production use.

### Which subnet tags does the EKS cluster example need?

The example sets kubernetes.io/cluster/${module.label.id} to shared, which is required for EKS and Kubernetes to discover and manage networking resources, plus kubernetes.io/role/elb on the public subnets and kubernetes.io/role/internal-elb on the private subnets, which the comment says are required to make ALB ingress work.

### Does this module provision worker nodes?

Not by itself. It provisions the control plane, and the README points at terraform-aws-eks-node-group and terraform-aws-eks-fargate-profile for the rest, saying that using the terraform-aws-eks-workers module is now rarely a better choice than the node group module.

## Sources

- [cloudposse/terraform-aws-eks-cluster on GitHub](https://github.com/cloudposse/terraform-aws-eks-cluster)
- [License: Apache-2.0](https://github.com/cloudposse/terraform-aws-eks-cluster/blob/main/LICENSE)
- [Project website](https://cloudposse.com/accelerate)
- [README](https://github.com/cloudposse/terraform-aws-eks-cluster/blob/main/README.md)
- [Releases](https://github.com/cloudposse/terraform-aws-eks-cluster/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cloudposse-terraform-aws-eks-cluster
