| Tested on | Ubuntu 26.04 LTS (Resolute Raccoon) |
|---|---|
| Package | terraform 1.16.1terraform-aws-modules/eks 21.25.0hashicorp/aws 6.63.0awscli 2.36.38kubectl 1.36 |
| Applies to | Any host with Terraform, AWS CLI, and kubectl; EKS clusters created with terraform-aws-modules/eks v20+ in ap-south-1 |
| Privilege | AWS identity that can describe the EKS cluster and manage access entries; normal user on the workstation |
| Scope | Fix error: You must be logged in to the server (Unauthorized) after a successful terraform apply on Amazon EKS — confirm which IAM principal kubectl uses, inspect EKS access entries, and grant API access with enable_cluster_creator_admin_permissions or access_entries. Does not cover building the VPC/EKS stack from scratch, IRSA, or kubeconfig file syntax. |
| Related guides | install kubectl and kubeconfig Fix Terraform AWS UnauthorizedOperation |
terraform apply finished and the EKS cluster exists. You ran aws eks update-kubeconfig, but kubectl still fails.
You may see either of these messages:
error: You must be logged in to the server (the server has asked for the client to provide credentials)error: You must be logged in to the server (Unauthorized)Both indicate that kubectl could not authenticate successfully to the Kubernetes API. On EKS, a common cause after Terraform creates the cluster is that the AWS principal used by kubectl does not have the required EKS access entry or access policy. Expired or mismatched AWS credentials can produce similar symptoms, so confirm the active AWS identity first.
Terraform did not fail — the cluster exists. You are debugging authentication or EKS API access, not a failed apply.
What kubectl Unauthorized means after EKS apply
Before kubectl can list nodes, EKS must authenticate your AWS identity and authorize the API call. A successful terraform apply only proves AWS accepted Terraform's API calls. It does not automatically grant your workstation cluster-admin access.
On terraform-aws-modules/eks v20 and later:
- Current module versions prefer EKS access entries for human and automation access
- The module no longer manages legacy
aws-authuser mappings the way older v19-era examples did - Because the default authentication mode is
API_AND_CONFIG_MAP, legacyaws-authmappings may still work, but they are not the preferred Terraform-managed path for new access
Common reasons you see Unauthorized right after apply:
enable_cluster_creator_admin_permissionsis missing orfalsekubectluses a different IAM user, role, or SSO profile than Terraform used duringapply- You upgraded the EKS module from v19 without adding
access_entriesfor humans or CI roles - Temporary credentials from
aws loginexpired betweenapplyandkubectl
If aws eks update-kubeconfig itself fails, fix AWS credentials first — see Fix Terraform AWS credentials error. This article assumes the kubeconfig context was created and the API endpoint is reachable.
enable_cluster_creator_admin_permissions = true so kubectl works immediately. You land here when that line is missing or when another identity runs kubectl.
Confirm which AWS identity kubectl uses
kubectl on EKS authenticates through the AWS CLI get-token plugin. That token must come from an IAM principal that has an EKS access entry (and the right access policy).
Run this in the same shell where you run kubectl:
aws sts get-caller-identitySample output for an account root identity:
{
"UserId": "342524208658",
"Account": "342524208658",
"Arn": "arn:aws:iam::342524208658:root"
}aws sts get-caller-identity shows the AWS identity currently used by the shell. For IAM users and root, the Arn can be compared directly with an access entry. For assumed roles or SSO sessions, STS returns an assumed-role/... session ARN such as arn:aws:sts::123456789012:assumed-role/AdminRole/deepak — the EKS access entry must instead contain the corresponding permanent IAM role ARN (arn:aws:iam::123456789012:role/AdminRole). EKS cannot use a temporary STS session principal as an access-entry target.
What to read in the JSON:
Arn— active identity for this shell; compare directly for IAM users and root, map to the permanent role ARN for assumed-role sessionsAccount— your 12-digit AWS account IDUserId— internal AWS identifier; useful when comparing with console users
Compare that identity to whoever ran terraform apply. If you use named profiles, check the profile Terraform used:
aws sts get-caller-identity --profile terraform-adminThen check the profile your shell uses for kubectl:
aws sts get-caller-identity --profile daily-userWhen the ARNs differ, Terraform may have created the cluster as terraform-admin while kubectl runs as daily-user. Only the identity that runs terraform apply gets an access entry from enable_cluster_creator_admin_permissions.
List EKS access entries on the cluster
On clusters using EKS API authentication, access entries associate IAM principals with Kubernetes access. The current terraform-aws-modules/eks v21 default authentication mode is API_AND_CONFIG_MAP, so access entries are the preferred path for human and automation access while legacy aws-auth mappings can still coexist.
Replace the cluster name and region with yours from terraform output or the AWS console.
List every principal that can reach the API:
aws eks list-access-entries --cluster-name glc-eks-unauth-lab --region ap-south-1Before the fix, only the managed node group role appeared — your administrative identity was missing:
{
"accessEntries": [
"arn:aws:iam::342524208658:role/lab-eks-node-group-82f09dee4228ef321fa1bba704"
]
}Why that causes Unauthorized:
- The managed node-group role may already appear as an access entry, while your administrative identity is missing
- Service-linked roles cannot be EKS access-entry principals — they will not appear here
kubectlhas no entry for your IAM user or role, so the API rejects the call
After you add cluster-creator admin permissions, run the same list command again:
aws eks list-access-entries --cluster-name glc-eks-unauth-lab --region ap-south-1Your administrative principal should now appear:
{
"accessEntries": [
"arn:aws:iam::342524208658:role/lab-eks-node-group-82f09dee4228ef321fa1bba704",
"arn:aws:iam::342524208658:root"
]
}The new line is the permanent IAM ARN granted access (root in this lab run). For assumed-role sessions, look for the permanent role ARN — not the assumed-role/... string from get-caller-identity.
To see details for one principal, use describe-access-entry with the ARN from the list:
aws eks describe-access-entry --cluster-name glc-eks-unauth-lab --principal-arn arn:aws:iam::342524208658:root --region ap-south-1Sample output:
{
"accessEntry": {
"clusterName": "glc-eks-unauth-lab",
"principalArn": "arn:aws:iam::342524208658:root",
"kubernetesGroups": [],
"type": "STANDARD",
"username": "arn:aws:iam::342524208658:root"
}
}Fields that matter:
principalArn— the permanent IAM user or role ARN granted cluster access; for assumed-role sessions, it will not literally equal the STS session ARN fromget-caller-identitytype—STANDARDfor IAM users and roleskubernetesGroups— often empty when you use EKS access policies instead of RBAC groups
Cluster access policies attach separately. The EKS module creates both the access entry and the AmazonEKSClusterAdminPolicy association when you enable cluster-creator permissions.
Fix with enable_cluster_creator_admin_permissions
Use this when the same identity runs terraform apply and kubectl. Add one line inside the EKS module block:
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 21.25"
name = local.cluster_name
kubernetes_version = "1.36"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
eks_managed_node_groups = {
lab = {
instance_types = ["t3.small"]
min_size = 1
max_size = 1
desired_size = 1
}
}
enable_cluster_creator_admin_permissions = true
}What that line does:
- Creates an EKS access entry for the IAM principal running
terraform apply - Attaches
AmazonEKSClusterAdminPolicyat cluster scope - Replaces the old automatic
aws-authmapping from EKS module v19
From your Terraform project directory, apply the change:
terraform applyReview the plan before you type yes. You should see two new resources:
# module.eks.aws_eks_access_entry.this["cluster_creator"] will be created
# module.eks.aws_eks_access_policy_association.this["cluster_creator_admin"] will be createdA successful apply ends with a line like Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Access-entry changes are eventually consistent. If kubectl still returns Unauthorized immediately after apply, wait a few seconds and retry before changing the configuration again.
Refresh kubeconfig if you changed regions or profiles:
aws eks update-kubeconfig --region ap-south-1 --name glc-eks-unauth-labupdate-kubeconfig normally prints the context it added or updated:
Added new context arn:aws:eks:ap-south-1:342524208658:cluster/glc-eks-unauth-lab to /root/.kube/configConfirm API access with:
kubectl get nodesSample output:
NAME STATUS ROLES AGE VERSION
ip-10-0-2-222.ap-south-1.compute.internal Ready <none> 3m5s v1.36.3-eks-cb19647What to look for:
STATUSisReady— the node joined the cluster- No Unauthorized error — your access entry is working
Fix with access_entries for other principals
enable_cluster_creator_admin_permissions only covers whoever runs terraform apply. Everyone else needs an explicit entry.
Who needs their own access_entries block:
- CI/CD IAM roles that run
kubectlor Helm - Teammates signing in through IAM roles or SSO (use the permanent role ARN)
- Break-glass admin roles
IAM users are supported, but AWS recommends IAM roles with temporary credentials for people and automation. Add one block per IAM role (or user):
access_entries = {
deploy-role = {
principal_arn = "arn:aws:iam::342524208658:role/deploy-role"
policy_associations = {
admin = {
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
access_scope = {
type = "cluster"
}
}
}
}
}Policy choices:
AmazonEKSClusterAdminPolicy— full cluster admin (lab and break-glass)AmazonEKSViewPolicy— read-only API access- Namespace-scoped
access_scope— limit access to one namespace in production
Run terraform apply after you add entries.
Emergency CLI grant (without Terraform)
An administrator can grant access immediately, then backport the change into .tf:
Create the access entry:
aws eks create-access-entry --cluster-name glc-eks-unauth-lab --principal-arn arn:aws:iam::342524208658:role/deploy-role --region ap-south-1Attach the cluster admin policy:
aws eks associate-access-policy --cluster-name glc-eks-unauth-lab --principal-arn arn:aws:iam::342524208658:role/deploy-role --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy --access-scope type=cluster --region ap-south-1Import CLI-created resources into Terraform later — otherwise the next terraform apply may try to recreate them.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
You must be logged in to the server (Unauthorized) |
Missing EKS access entry or policy for the IAM principal behind kubectl |
Add enable_cluster_creator_admin_permissions or access_entries; confirm with aws eks list-access-entries |
the server has asked for the client to provide credentials |
Expired credentials, token failure, or missing access entry | Confirm aws sts get-caller-identity; refresh aws login credentials; then check access entries |
terraform apply user works, teammate fails |
Only cluster creator received admin, or teammate uses assumed-role ARN without matching permanent role entry | Add access_entries for the teammate's permanent IAM role ARN |
| Access entry exists, still Unauthorized | Missing policy association, propagation delay, or wrong policy scope | Wait a few seconds and retry; run aws eks list-associated-access-policies for that principal |
| Worked yesterday on module v19 | Module no longer manages legacy aws-auth mappings the old way |
Prefer access entries; legacy aws-auth may still work under API_AND_CONFIG_MAP but is not the Terraform-managed path |
update-kubeconfig fails |
AWS credentials or cluster name/region mismatch | Fix credentials guide; verify terraform output cluster name |
For broader Terraform failure stages, see Terraform troubleshooting.
Summary
terraform apply on EKS can succeed while kubectl still returns Unauthorized. The cluster is healthy — your IAM principal may be missing from the EKS access list, or kubectl may be using expired or mismatched AWS credentials.
After terraform-aws-modules/eks v20, access entries are the preferred Terraform-managed path for human and automation access. Legacy aws-auth mappings can still coexist when the cluster uses API_AND_CONFIG_MAP.
Fix in short:
- Run
aws sts get-caller-identityin the shell that runskubectl— map assumed-role sessions to the permanent IAM role ARN - Run
aws eks list-access-entriesand confirm that permanent ARN is listed - Add
enable_cluster_creator_admin_permissions = truewhen the same principal runs Terraform andkubectl - Add
access_entriesfor every other IAM role (or user) that needs API access - Re-run
terraform apply, wait a few seconds for propagation, thenkubectl get nodesand look forReady
Keep access entries in Terraform so module upgrades and cluster recreations do not silently remove API access.
References
- Amazon EKS access entries
- terraform-aws-modules/eks/aws — v20 upgrade notes
- AWS CLI — eks list-access-entries
- AWS CLI — eks update-kubeconfig
- Kubernetes — Organizing Cluster Access Using kubeconfig Files
Frequently Asked Questions
Is this the same as No valid credential sources found?
No. Missing AWS credentials stop Terraform or the get-token plugin before EKS is contacted. Unauthorized means AWS signed the request, but EKS denied API access for that principal.
Do I still edit the aws-auth ConfigMap?
On new clusters with API_AND_CONFIG_MAP, legacy aws-auth mappings may still work, but access entries are the preferred Terraform-managed path. The v21 module does not manage mapUsers the way older v19 examples did.
Why did enable_cluster_creator_admin_permissions fix only one user?
It grants admin to the IAM principal that runs terraform apply. Everyone else needs their own access_entries block.
Can I use mapUsers in aws-auth instead?
Only when the cluster authentication mode still includes the ConfigMap (API_AND_CONFIG_MAP or CONFIG_MAP) and you deliberately manage it outside Terraform. For new access, use access_entries in the EKS module.
kubectl works for get nodes but not helm install
The principal may have view access only. Associate AmazonEKSClusterAdminPolicy or a narrower policy that includes the API groups Helm needs.

