Ditch the aws-auth ConfigMap: Managing EKS Access with Terraform Access Entries and Policies

If you’ve been working with Amazon EKS for a while, you probably have a love-hate (mostly hate) relationship with the aws-auth ConfigMap. It was the original way to map AWS IAM Users and Roles to Kubernetes RBAC groups.
But let’s be honest: it was flawed. Editing a central YAML file meant easy typos could lock out your entire team. It suffered from race conditions when updated programmatically, was tricky to manage via Infrastructure as Code (IaC) without overwriting manual changes, and left no native AWS CloudTrail audit logs for access modifications.
Fortunately, AWS heard the community’s cries. With recent EKS updates, managing access has become a first-class citizen in the AWS API. In this post, we’ll dive into the new EKS API authentication mode and see how to elegantly manage access using Terraform’s aws_eks_access_entry resource. We will build a tiered access model featuring a read-only “developer” and a cluster-wide “admin.” By the end, you’ll know how to enable the API authentication mode, map IAM Users and Roles to Kubernetes groups, and set up the Kubernetes RBAC to complete the chain.
Conceptual Overview: From Shared Spreadsheet to Individual Keycards
Before we touch any code, let’s understand what changed conceptually.
The old aws-auth ConfigMap was like a shared spreadsheet pinned to the office wall. Every time someone needed access to the Kubernetes cluster, an admin would walk over and scribble their name on the sheet. If two admins tried to update it at the same time, one would overwrite the other. If someone accidentally erased a line, an entire team would be locked out—with no record of who changed what.
EKS Access Entries replace this with an individual keycard system. Each person (IAM User or Role) gets their own keycard (an Access Entry) that is managed independently through the AWS API. Adding or revoking someone’s access doesn’t touch anyone else’s configuration. Every change is logged in AWS CloudTrail, giving you a full audit trail. And because each keycard is its own Terraform resource, your Infrastructure as Code can manage them cleanly without merge conflicts.
The New Way: EKS API Authentication
EKS now allows you to manage IAM principal access directly through AWS APIs (and therefore, Terraform resources) rather than manipulating a Kubernetes ConfigMap. This provides:
- No race conditions: API calls are handled safely by AWS.
- Granular IaC control: Each IAM-to-Kubernetes mapping is a separate Terraform resource.
- Auditability: Every change is logged in CloudTrail.
Step 1: Enabling API Authentication on the Cluster
To use this feature, your EKS cluster must be configured to use the API for authentication.
In our 7-eks.tf file, we enable this within the aws_eks_cluster resource using the access_config block:
resource "aws_eks_cluster" "eks" {
name = "${local.env}-${local.eks_name}"
version = local.eks_version
role_arn = aws_iam_role.eks.arn
vpc_config {
endpoint_private_access = false
endpoint_public_access = true
subnet_ids = [
aws_subnet.private_zone1.id,
aws_subnet.private_zone2.id
]
}
# The magic happens here
access_config {
authentication_mode = "API"
bootstrap_cluster_creator_admin_permissions = true
}
depends_on = [aws_iam_role_policy_attachment.eks]
}
[!NOTE] Setting
bootstrap_cluster_creator_admin_permissions = trueensures that the IAM principal creating the cluster (your CI/CD pipeline or your local user) automatically gets cluster admin rights.
Step 2: Tiered Access Strategy
A best practice for EKS is to implement tiered access. We don’t want everyone running around as cluster-admin. We’ll set up two tiers:
- Developer: An IAM User mapped to a restricted, read-only Kubernetes group (
my-viewer). - Manager / Admin: An IAM Role mapped to the powerful
cluster-admingroup (my-admin).
[!TIP] Why Roles over Users? While we use an IAM User for the developer in this demo, for production environments, you should almost always use IAM Roles (assumed via STS). Roles provide temporary credentials, removing the risk of long-lived access keys being leaked. Users can assume roles via SSO (like AWS IAM Identity Center) or OIDC providers.
Step 3: The Developer (Read-Only Access)
Let’s look at how we provision the developer access in 9-add-developer-user.tf. We create an IAM user, give them basic AWS permissions to list EKS clusters, and then use the new aws_eks_access_entry resource to tie them to a Kubernetes group called my-viewer.
resource "aws_iam_user" "developer" {
name = "developer"
}
resource "aws_iam_policy" "developer_eks" {
name = "AmazonEKSDeveloperPolicy"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"eks:DescribeCluster",
"eks:ListClusters"
],
"Resource": "*"
}
]
}
POLICY
}
resource "aws_iam_user_policy_attachment" "developer_eks" {
user = aws_iam_user.developer.name
policy_arn = aws_iam_policy.developer_eks.arn
}
# The Access Entry mapping IAM to Kubernetes
resource "aws_eks_access_entry" "developer" {
cluster_name = aws_eks_cluster.eks.name
principal_arn = aws_iam_user.developer.arn
kubernetes_groups = ["my-viewer"]
}
The Kubernetes RBAC Side
Inside Kubernetes, the my-viewer group needs permissions. We define a standard ClusterRole and bind it to the my-viewer group.
Here is the viewer-cluster-role.yaml:
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: viewer
rules:
- apiGroups: ["*"]
resources: ["deployments", "configmaps", "pods", "secrets", "services"]
verbs: ["get", "list", "watch"]
And the viewer-cluster-role-binding.yaml:
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-viewer-binding
roleRef:
kind: ClusterRole
name: viewer
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: my-viewer
apiGroup: rbac.authorization.k8s.io
Step 4: The Manager (Cluster Admin)
For admins, we follow a more secure pattern: an assumable IAM Role.
In 10-add-manager-role.tf, we create the eks_admin role and an IAM User (manager) who is allowed to assume it. Then, we map the Role ARN to the my-admin Kubernetes group.
# Create the Admin Role
resource "aws_iam_role" "eks_admin" {
name = "${local.env}-${local.eks_name}-eks-admin"
assume_role_policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
}
]
}
POLICY
}
# Give the Role broad EKS permissions
resource "aws_iam_policy" "eks_admin" {
name = "AmazonEKSAdminPolicy"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["eks:*"],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:PassedToService": "eks.amazonaws.com"
}
}
}
]
}
POLICY
}
# The Access Entry mapping the Admin Role to Kubernetes
resource "aws_eks_access_entry" "manager" {
cluster_name = aws_eks_cluster.eks.name
principal_arn = aws_iam_role.eks_admin.arn
kubernetes_groups = ["my-admin"]
}
The Admin RBAC
Kubernetes already has a built-in cluster-admin ClusterRole. All we need to do is bind our custom my-admin group to it.
admin-cluster-role-binding.yaml:
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-admin-binding
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: my-admin
apiGroup: rbac.authorization.k8s.io
Gotchas and Practical Tips
-
The migration from
CONFIG_MAPtoAPIis one-way. Once you switch your cluster’sauthentication_modefromCONFIG_MAPtoAPI(orAPI_AND_CONFIG_MAP), you cannot go back toCONFIG_MAPonly. Plan your migration carefully and test in a staging environment first. -
Group names must match exactly. The
kubernetes_groupsvalue in youraws_eks_access_entry(e.g.,"my-viewer") must be an exact, case-sensitive match with thesubjects[].namein your KubernetesClusterRoleBindingYAML. A mismatch likemy-viewervs.My-Viewerwill silently fail—the user will authenticate but have zero permissions.
Conclusion
Moving away from the aws-auth ConfigMap is one of the biggest operational improvements you can make to your EKS clusters today.
- Use
authentication_mode = "API": It’s native, reliable, and stops configuration drift and race conditions. - Treat mappings as distinct resources: Using
aws_eks_access_entryallows you to cleanly add or remove users/roles without fear of deleting someone else’s access. - Prefer IAM Roles: Map IAM Roles to Kubernetes groups, and let users authenticate via SSO/STS to assume those roles. It is significantly more secure than tying long-lived IAM Users directly to EKS.
- Use Custom Groups: Map your AWS identities to custom Kubernetes groups (like
my-viewerormy-admin), and handle the specific permission rules inside Kubernetes RBAC.
With these patterns, you can confidently automate and audit your EKS access lifecycle without ever touching a fragile ConfigMap again.