Terraform + Kubernetes configuration to provision a private EKS cluster on AWS and expose an nginx application via an internet-facing ALB.
-
Discussions to additional questions can be found under assignments.md
-
Sample NGINX app running here (to be terminated by 2026-05-31)
Disclaimer: This assignment is implemented with the assistance from Claude Code; all submitted code was reviewed, tested, and validated by me before submission.
and yes I caught Claude spit out something believable, but nonsense...
resource "aws_cloudwatch_metric_alarm" "pod_restarts" {
alarm_name = "${local.app_name}-pod-restarts-high"
alarm_description = "Pods restarting frequently — possible OOMKill, crash-loop, or failed liveness probe."
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "pod_number_of_container_restarts"
namespace = "ContainerInsights"
period = 300
statistic = "Sum"
threshold = 5
treat_missing_data = "notBreaching"
alarm_actions = local.alarm_actions
ok_actions = local.alarm_actions
dimensions = {
ClusterName = "${local.app_name}-eks"
Namespace = "default"
}
}
- VPC — 3 private subnets (nodes/pods), 3 public subnets (ALB), single NAT gateway (via terraform module)
- EKS — private API endpoint, managed node group (AL2023), Kubernetes 1.35 (via terraform module)
- Add-ons — vpc-cni, kube-proxy, coredns, aws-ebs-csi-driver
- Ingress — AWS Load Balancer Controller creates an internet-facing ALB from a Kubernetes
Ingressresource - CI/CD — GitHub Actions deploys via OIDC
- Terraform >= 1.14 (pinned via
.terraform-version) - AWS CLI configured with sufficient IAM permissions to provision vpc + eks
kubectlandhelm- AWS Cloud Shell
The GitHub Actions OIDC role must be created locally before CI can take over.
export AWS_PROFILE=hiive
# Init — backend doesn't support variable interpolation so profile is passed explicitly
terraform init -backend-config="profile=hiive"
# Create only the OIDC + IAM resources
terraform apply \
-target=aws_iam_openid_connect_provider.github \
-target=aws_iam_role.github_actions \
-target=aws_iam_policy.github_actions \
-target=aws_iam_role_policy_attachment.github_actions
# Add the role ARN as a secret in GitHub:
# repo → Settings → Secrets → Actions → AWS_ROLE_ARN
terraform output -raw github_actions_role_arnAfter bootstrap, push to main — GitHub Actions runs terraform apply automatically.
To apply locally:
AWS_PROFILE=hiive terraform applyTo override instance type:
AWS_PROFILE=hiive terraform apply -var="eks_node_instance_type=t3.medium"Since the EKS API endpoint is private, accessing eks via cloud-shell...
aws eks update-kubeconfig \
--region ca-central-1 \
--name hiive-eks
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# ip-10-0-2-181.ca-central-1.compute.internal Ready <none> 23h v1.35.4-eks-7fcd7echelm repo add eks https://aws.github.io/eks-charts
helm repo update
helm upgrade --install aws-load-balancer-controller eks/aws-load-balancer-controller \
--namespace kube-system \
--set clusterName=hiive-eks \
--set serviceAccount.create=true \
--set serviceAccount.name=aws-load-balancer-controller \
--set "serviceAccount.annotations.eks\.amazonaws\.com/role-arn=$(terraform output -raw load_balancer_controller_role_arn)" \
--set region=ca-central-1 \
--set vpcId=$(terraform output -raw vpc_id) \
--wait
kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
# NAME READY STATUS RESTARTS AGE
# aws-load-balancer-controller-5556b58d8d-md9xz 1/1 Running 0 14h
# aws-load-balancer-controller-5556b58d8d-p454x 1/1 Running 0 14h# Patch the ALB security group ID into the ingress annotation
# k8s/nginx.yaml → alb.ingress.kubernetes.io/security-groups
terraform output -raw alb_security_group_id
kubectl apply -f k8s/nginx.yaml
# Wait ~60s for the ALB to provision, then get the public URL
kubectl get ingress nginx
# NAME CLASS HOSTS ADDRESS PORTS AGE
# nginx alb * k8s-default-nginx-61aae4aedd-1684337437.ca-central-1.elb.amazonaws.com 80 14hOpen the ADDRESS in a browser to see the nginx splash page.
| Event | Action |
|---|---|
Pull request to main |
terraform plan |
Push to main |
terraform apply |
Workflow: .github/workflows/terraform.yml
Authentication is via GitHub Actions OIDC
kubectl delete -f k8s/nginx.yaml
AWS_PROFILE=hiive terraform destroy| Name | Version |
|---|---|
| terraform | ~> 1.14 |
| aws | ~> 6.28 |
| Name | Version |
|---|---|
| aws | ~> 6.28 |
| Name | Source | Version |
|---|---|---|
| vpc | terraform-aws-modules/vpc/aws | 6.6.1 |
| eks | terraform-aws-modules/eks/aws | 21.20.0 |
| irsa-ebs-csi | terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc | 5.52.0 |
| irsa-aws-load-balancer-controller | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | 5.52.0 |
| Name | Type |
|---|---|
| aws_security_group.alb | resource |
| aws_iam_openid_connect_provider.github | resource |
| aws_iam_role.github_actions | resource |
| aws_iam_policy.github_actions | resource |
| aws_iam_role_policy_attachment.github_actions | resource |
| aws_caller_identity.current | data source |
| aws_availability_zones.available | data source |
| aws_iam_policy.ebs_csi_policy | data source |
| aws_iam_policy_document.github_actions | data source |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| region | AWS region | string |
"ca-central-1" |
no |
| eks_node_instance_type | EC2 instance type for EKS managed node group | string |
"t3.small" |
no |
| Name | Description |
|---|---|
| cluster_endpoint | Endpoint for EKS control plane |
| cluster_security_group_id | Security group IDs attached to the cluster control plane |
| cluster_name | Kubernetes cluster name |
| region | AWS region |
| vpc_id | VPC ID |
| alb_security_group_id | Security group ID to assign to the nginx ALB via ingress annotation |
| load_balancer_controller_role_arn | IAM role ARN for the AWS Load Balancer Controller |
| github_actions_role_arn | IAM role ARN for GitHub Actions OIDC |