The kubernetes provider in the eks-quickstart, eks-addons-access and eks-https-ingress workbooks was configured from the managed cluster resource (host = aws_eks_cluster.this.endpoint). On destroy the endpoint resolved empty, the provider fell back to localhost:80, and refreshing kubernetes_deployment_v1.hello / kubernetes_service_v1.hello failed with 'dial tcp [::1]:80: connect: connection refused'. Move the kubernetes provider and the hello Deployment + Service into a nested workloads/ root module with its own state. It reads cluster identity from the parent via terraform_remote_state and the live endpoint/CA from a data aws_eks_cluster source, so the provider is only ever configured while the cluster exists. Apply parent then workloads; destroy workloads then parent.
title, description, category, tags, resources
| title | description | category | tags | resources | |||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| EKS with Addons + Access-Entry RBAC | Extend a single-node EKS cluster with a managed Argo CD addon, access-entry RBAC, and a browsable load-balanced demo app, using Terraform on Spinifex. | Terraform Workbooks |
|
|
Terraform: EKS with Addons + Access-Entry RBAC
Builds on the quickstart by installing a managed addon (Argo CD) and granting a second IAM principal read-only cluster access — then deploys the same browsable, load-balanced demo app so you can see the result.
Table of Contents
Overview
This workbook takes the same single-node cluster (and the same browsable demo app) from EKS Quickstart and adds the two things you reach for on almost any real cluster:
- A managed addon. Argo CD is installed through the EKS API (
aws_eks_addon) rather thankubectl apply, so its lifecycle is managed by the cluster. Spinifex stages the addon's manifests host-side, then the worker renders them into the K3s auto-deploy dir. The demo app is deployed after the addon reachesACTIVE, so a healthy page also confirms the addon delivery pipeline worked end to end. - Access-entry RBAC. A second IAM role is granted read-only access via an
aws_eks_access_entryplus anaws_eks_access_policy_associationbound to the AWS-managedAmazonEKSViewPolicy. This is the modern access-entry path (authentication_mode = "API") — noaws-authConfigMap.
When apply finishes, open the demo_url output and refresh — the nginxdemos/hello page reports which pod served each request.
What you'll learn:
- Installing a managed EKS addon with
aws_eks_addon - Registering an IAM principal with
aws_eks_access_entryand scoping it withaws_eks_access_policy_association - The difference between the cluster creator's admin access and a scoped viewer
- Deploying a workload with the Kubernetes provider once the addon is healthy
What gets created
| Resource | Name | Purpose |
|---|---|---|
| VPC | eks-addons-vpc |
Isolated network (10.31.0.0/16) |
| Subnets | eks-addons-subnet-a/-b |
Public subnets for the cluster and worker |
| Internet Gateway | eks-addons-igw |
Egress so the worker can pull the demo image |
| IAM Roles | eks-addons-cluster-role, eks-addons-node-role |
Control-plane and worker roles |
| IAM Role | eks-addons-viewer |
Stand-in principal granted read-only access |
| EKS Cluster | eks-addons |
Public endpoint, API auth mode |
| Node Group | default |
One t3.medium worker |
| Addon | argocd |
GitOps CD, managed via the EKS API |
| Access Entry + Assoc. | eks-addons-viewer → AmazonEKSViewPolicy |
Read-only, cluster scope |
| SG Ingress Rule | eks-addons-demo-nodeport |
Opens the demo NodePort on the worker SG |
| K8s Deployment + Service | hello |
nginxdemos/hello, 2 replicas, NodePort |
Spinifex specifics
- Addon catalog is limited. Spinifex currently serves
argocd,coredns,aws-load-balancer-controllerandspinifex-noop. There is novpc-cni,kube-proxy,ebs-csiormetrics-server. Only addons with a baked bundle shipped in theeks-nodeAMI install successfully —argocdandspinifex-noopare the bundles shipped today. - Leave
addon_versionunset. Spinifex defaults an unset version to its catalog default (argocd3.0.23). Pinning is a dead end: the AWS provider rejects a bare3.0.23(it requires thev-prefixedv3.0.23-eksbuild.Nform), while Spinifex's catalog only accepts the bare3.0.23— so no single string satisfies both. - Cluster access policies are the four AWS-managed
cluster-access-policy/Amazon EKS{ClusterAdmin,Admin,Edit,View}PolicyARNs. - The worker SG is auto-managed and the worker AMI is always the
eks-nodeimage; the NodePort is exposed by adding one rule to the looked-up worker SG.
Prerequisites:
- Spinifex installed and running
- The Spinifex
eks-nodeimage available on the cluster - OpenTofu or Terraform, plus
kubectland the AWS CLI
Instructions
Step 1. Get the Template
git clone --depth 1 --filter=blob:none --sparse https://github.com/mulgadc/spinifex.git spinifex-tf
cd spinifex-tf
git sparse-checkout set docs/terraform-workbooks
cd docs/terraform-workbooks/eks-addons-access
Or create a main.tf file and paste the full configuration below.
Step 2. Deploy
The cluster and the demo app are two root modules with separate state. Apply the cluster first:
export AWS_PROFILE=spinifex
tofu init
tofu apply
Allow a few minutes for the cluster to reach ACTIVE, the worker to register, and Argo CD to install. Then deploy the demo app from the nested workloads/ module:
cd workloads
tofu init
tofu apply
Why two modules? The Kubernetes provider in
workloads/reads the cluster endpoint from a livedata "aws_eks_cluster"source, so it's only ever configured while the cluster exists. Keeping it out of the cluster module meansdestroynever tries to refresh a workload against a cluster that's already gone — the failure mode where the provider falls back tohttp://localhost:80and reportsconnection refused. Always destroyworkloads/before the cluster.
Keep
AWS_PROFILE=spinifexexported for both applies — the Kubernetes provider runsaws eks get-tokenagainst the Spinifex STS endpoint.
Step 3. Open the Demo
Run from the cluster module directory (cd .. if you're still in workloads/):
tofu output demo_url
Open it in a browser and refresh — the nginxdemos/hello page shows the pod that answered, alternating between the two replicas.
Step 4. Verify the Addon and Access Entry
aws eks list-addons --cluster-name eks-addons
aws eks describe-addon --cluster-name eks-addons --addon-name argocd --query 'addon.status'
aws eks list-access-entries --cluster-name eks-addons
aws eks list-associated-access-policies \
--cluster-name eks-addons \
--principal-arn "$(tofu output -raw viewer_principal_arn)"
The viewer principal is bound to AmazonEKSViewPolicy at cluster scope — it can read across the cluster but cannot mutate anything. The principal that ran apply keeps full cluster-admin from bootstrap_cluster_creator_admin_permissions.
Cleanup
Destroy in reverse — the demo app first (while the cluster is still up), then the cluster:
cd workloads
tofu destroy
cd ..
tofu destroy
Troubleshooting
Demo URL Doesn't Load
Same chain as the quickstart — check the worker's public IP, that the demo pods are Running, and that the NodePort rule landed on the worker SG:
kubectl get pods -o wide
aws ec2 describe-security-groups --filters "Name=group-name,Values=eks-cluster-eks-addons-nodegroup-sg" \
--query 'SecurityGroups[0].IpPermissions'
If the addon never reaches ACTIVE, confirm Argo CD's pods came up on the worker:
kubectl -n argocd get pods
kubectl -n argocd get deploy
Addon Stuck in CREATING / DEGRADED
Argo CD needs a schedulable worker with headroom (it runs ~6 pods). Confirm the node group is ACTIVE and the node is Ready:
aws eks describe-nodegroup --cluster-name eks-addons --nodegroup-name default --query 'nodegroup.status'
kubectl get nodes
Addon Version Rejected
This workbook leaves addon_version unset so Spinifex picks its catalog default. If you add an explicit addon_version, expect a wall: the AWS provider requires the v-prefixed v3.0.23-eksbuild.N form, but Spinifex's catalog only knows the bare 3.0.23 — a value that passes one is rejected by the other. Prefer omitting the field.
Addon Fails With "no baked bundle"
Spinifex only installs addons whose manifests are baked into the eks-node AMI. If describe-addon reports CREATE_FAILED with no baked bundle for version <v>, the running worker image predates that addon. Rebuild and republish the eks-node image so the bundle ships, then recreate the addon.
Access Entry Not Taking Effect
Assume the viewer role and confirm it can read but not write:
kubectl auth can-i list pods --all-namespaces # yes
kubectl auth can-i create deployments # no
Provider Connection Refused
sudo systemctl status spinifex.target
curl -k https://localhost:9999/