🟢 Welcome to my World!
WORKSPACE
TIMELINE
root/welcome.hi

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

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 = true ensures 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:

  1. Developer: An IAM User mapped to a restricted, read-only Kubernetes group (my-viewer).
  2. Manager / Admin: An IAM Role mapped to the powerful cluster-admin group (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

  1. The migration from CONFIG_MAP to API is one-way. Once you switch your cluster’s authentication_mode from CONFIG_MAP to API (or API_AND_CONFIG_MAP), you cannot go back to CONFIG_MAP only. Plan your migration carefully and test in a staging environment first.

  2. Group names must match exactly. The kubernetes_groups value in your aws_eks_access_entry (e.g., "my-viewer") must be an exact, case-sensitive match with the subjects[].name in your Kubernetes ClusterRoleBinding YAML. A mismatch like my-viewer vs. My-Viewer will 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.

  1. Use authentication_mode = "API": It’s native, reliable, and stops configuration drift and race conditions.
  2. Treat mappings as distinct resources: Using aws_eks_access_entry allows you to cleanly add or remove users/roles without fear of deleting someone else’s access.
  3. 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.
  4. Use Custom Groups: Map your AWS identities to custom Kubernetes groups (like my-viewer or my-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.

TERMINAL
1: zsh
sirishgurung@portfolio:-$cat ./contact.txt
▄▄   ▄▄▄▄ ▄▄▄▄ ▄▄  ▄▄▄      ▄▄ ▄ ▄ ▄▄▄▄ ▄▄▄▄ ▄▄ ▄      ▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄ ▄ ▄▄▄▄ ▄▄▄▄ ▄▄
██   ██ ▀  ██  ▀  ██▀       ██ █ █ ██ █ ██ █ ██ █       ██  ██ █ ██    ██ ▀  ██  ██ █ ██ ▀ ██ █ ██
██   ██▀   ██     ▀██▄      ██ █ █ ██ █ ██▄▀ ██▄▀       ██  ██ █ ██ ▄▄ ██▀   ██  ██▄█ ██▀  ██▄▀ ██
 █▄▄  █ █  ▐█      ▄▀▀       █ █ ▀  █ █ ▀█ █  █ █       ▐█   █ █ ▐▀ ▀▌  █ █  ▐█   █ █  █ █ ▀█ █ ▀▀
▀▀▀▀ ▀▀▀▀  ▀▀     ▀▀▀       ▀▀▀▀▀▀ ▀▀▀▀ ▀▀ ▀ ▀▀ ▀       ▀▀  ▀▀▀▀ ▀▀▀▀▀ ▀▀▀▀  ▀▀  ▀▀ ▀ ▀▀▀▀ ▀▀ ▀ ▀▀
      
I'd love to hear from you!>Get in touch