Files
spinifex/docs/terraform-workbooks/eks-quickstart/README.md
T
Josh Siviour 67092d40be fix(workbooks): split EKS demo workloads into separate root module (#370)
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.
2026-06-16 21:48:56 +10:00

7.9 KiB

title, description, category, tags, resources
title description category tags resources
EKS Quickstart Stand up a minimal managed-Kubernetes cluster and a browsable demo app — VPC, IAM roles, an EKS cluster, a single-worker node group, and a load-balanced web page — using Terraform on Spinifex. Terraform Workbooks
terraform
eks
kubernetes
iam
vpc
workbook
title url
Terraform AWS Provider https://registry.terraform.io/providers/hashicorp/aws/latest
title url
Terraform Kubernetes Provider https://registry.terraform.io/providers/hashicorp/kubernetes/latest
title url
Spinifex Repository https://github.com/mulgadc/spinifex
title url
OpenTofu https://opentofu.org/

Terraform: EKS Quickstart

The smallest end-to-end EKS example on Spinifex: a VPC, the two IAM roles EKS needs, a single-worker cluster, and a demo web app you can open in a browser.

Table of Contents


Overview

Provision a managed Kubernetes cluster on Spinifex with Terraform/OpenTofu — and finish with something you can actually see. As well as the cluster, this workbook deploys a small demo app onto it and publishes it on the worker's public IP. When apply finishes, open the demo_url output: the page is served by nginxdemos/hello and prints the name of the pod that answered. Refresh it and the answering pod changes, so even a non-technical viewer can watch Kubernetes scheduling and load-balancing in real time.

Under the hood it keeps things minimal: a VPC with two public subnets, an IAM role for the control plane, an IAM role for the workers with the three managed policies AWS-managed node groups expect, a public-endpoint aws_eks_cluster, and a single-node aws_eks_node_group. Terraform then uses the Kubernetes provider (authenticating exactly like kubectl does, via aws eks get-token) to deploy the Deployment + NodePort Service.

What you'll learn:

  • Configuring the AWS provider to target Spinifex's eks, ec2, iam and sts endpoints
  • Creating the EKS cluster and worker IAM roles with faithful managed-policy attachments
  • Provisioning an aws_eks_cluster and a managed aws_eks_node_group
  • Pointing the Kubernetes provider at the new cluster and deploying a workload from the same apply
  • Opening one rule on the auto-managed worker security group to expose a NodePort

What gets created

Resource Name Purpose
VPC eks-quickstart-vpc Isolated network (10.30.0.0/16)
Subnets eks-quickstart-subnet-a/-b Public subnets for the cluster and worker
Internet Gateway eks-quickstart-igw Egress so the worker can pull the demo image
IAM Roles eks-quickstart-cluster-role, eks-quickstart-node-role Control-plane and worker roles
EKS Cluster eks-quickstart Public API endpoint, API auth mode, Kubernetes 1.32
Node Group default One t3.medium worker
SG Ingress Rule eks-quickstart-demo-nodeport Opens the demo NodePort on the auto-managed worker SG
K8s Deployment hello nginxdemos/hello, 2 replicas
K8s Service hello NodePort publishing the demo on the worker

Spinifex specifics

  • The cluster's security groups are auto-managed — vpc_config.security_group_ids is ignored. To reach a NodePort, this workbook looks the worker SG up by its deterministic name (eks-cluster-<name>-nodegroup-sg) and adds a single ingress rule.
  • The worker AMI is always Spinifex's eks-node image; ami_type is recorded but does not select the image.
  • authentication_mode must be "API" (the API_AND_CONFIG_MAP mode is rejected).
  • The workers need outbound internet (here via the IGW) to pull the demo container image.

Prerequisites:

  • Spinifex installed and running (see Installing Spinifex)
  • The Spinifex eks-node image available on the cluster
  • OpenTofu or Terraform, plus kubectl and 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-quickstart

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

This creates the cluster (which bootstraps a control-plane VM and brings up k3s — a few minutes in CREATING), launches the worker, and opens the NodePort. Once it's ACTIVE, 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 live data "aws_eks_cluster" source, so it's only ever configured while the cluster exists. Keeping it out of the cluster module means destroy never tries to refresh a workload against a cluster that's already gone — the failure mode where the provider falls back to http://localhost:80 and reports connection refused. Always destroy workloads/ before the cluster.

Same profile for the Kubernetes provider. The Kubernetes provider authenticates by shelling out to aws eks get-token, which has to reach the Spinifex STS endpoint. Keep AWS_PROFILE=spinifex exported for both applies.

Step 3. Open the Demo

Run from the cluster module directory (cd .. if you're still in workloads/):

tofu output demo_url

Open that URL in a browser. You'll see the nginxdemos/hello page showing the Server name (the pod that handled the request). Refresh a few times — with two replicas, the pod name alternates, demonstrating that the Service is load-balancing across the cluster.

Step 4. Inspect with kubectl (optional)

aws eks update-kubeconfig --name eks-quickstart --region ap-southeast-2
kubectl get nodes
kubectl get pods -o wide

kubectl get pods -o wide shows the demo pods and which node each landed on.

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

The page is served from a NodePort on the worker's public IP. Work through the chain:

# Is the worker running with a public IP?
aws ec2 describe-instances --filters "Name=tag:spinifex:eks-cluster,Values=eks-quickstart" \
  --query 'Reservations[].Instances[].[InstanceId,State.Name,PublicIpAddress]' --output text

# Did the demo pods roll out?
kubectl get pods -o wide

# Is the NodePort rule on the worker SG?
aws ec2 describe-security-groups --filters "Name=group-name,Values=eks-cluster-eks-quickstart-nodegroup-sg" \
  --query 'SecurityGroups[0].IpPermissions'

If the pods are Pending, the worker may not be Ready yet — give it a moment. If they're ImagePullBackOff, the worker can't reach Docker Hub; confirm the IGW route and the worker's public IP.

kubernetes provider: connection refused / Unauthorized

The provider runs aws eks get-token against the Spinifex STS endpoint. Confirm the AWS CLI is still pointed at Spinifex and the cluster is ACTIVE:

aws sts get-caller-identity
aws eks describe-cluster --name eks-quickstart --query 'cluster.status'

If the cluster was still CREATING when the provider first tried to connect, just re-run tofu apply.

Cluster Stuck in CREATING

Control-plane bootstrap takes a few minutes. Confirm the underlying VM came up:

aws eks describe-cluster --name eks-quickstart --query 'cluster.status'
aws ec2 describe-instances --profile spinifex

Provider Connection Refused

sudo systemctl status spinifex.target
curl -k https://localhost:9999/