AWS Controllers for Kubernetes (ACK) Getting Started

Modern cloud-native applications often depend on managed cloud services like Amazon S3, DynamoDB, RDS, and IAM roles. Traditionally, provisioning these resources required coordination between application and platform engineering teams.
AWS Controllers for Kubernetes (ACK) allows you to define and manage AWS resources directly via Kubernetes Custom Resources (CRDs).
This guide walks through configuring ACK on Amazon EKS using EKS Pod Identity instead of legacy IRSA.
📁 Source Code: aws-eks-terraform / eks-auto-ACK
📺 Video Walkthrough
Architecture
graph TD
CR["Kubernetes CR (e.g. S3 Bucket / IAM Role)"] --> Controller["ACK Service Controller"]
Controller --> Agent["EKS Pod Identity Agent"]
Agent --> STS["AWS STS (AssumeRole)"]
Controller -- "AWS API Calls" --> AWS["AWS Services (S3, IAM, DynamoDB)"]
Each AWS service has its own dedicated ACK controller (e.g. iam-controller, s3-controller, rds-controller) available as an OCI Helm chart in the Amazon ECR Public Gallery.
Step-by-Step Implementation
Step 1. Create IAM Role with Pod Identity for the IAM Controller
The ACK IAM controller requires permissions to manage IAM roles, policies, and trust relationships.
ack-iam-controller-role.tf
data "aws_iam_policy_document" "pod_id_assume_role" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["pods.eks.amazonaws.com"]
}
actions = [
"sts:AssumeRole",
"sts:TagSession"
]
}
}
resource "aws_iam_role" "ack_iam_controller" {
name = "ack-iam-controller-${module.eks.cluster_name}"
assume_role_policy = data.aws_iam_policy_document.pod_id_assume_role.json
}
resource "aws_iam_policy" "ack_iam_controller" {
name = "ack-iam-controller-policy-${module.eks.cluster_name}"
description = "Permissions for ACK IAM Controller"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowRoleManagement"
Effect = "Allow"
Action = [
"iam:CreateRole",
"iam:TagRole",
"iam:PassRole",
"iam:UpdateAssumeRolePolicy",
"iam:GetRole",
"iam:ListRolePolicies",
"iam:PutRolePolicy",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:ListRoles",
"iam:ListRoleTags",
"iam:AttachRolePolicy",
"iam:DetachRolePolicy",
"iam:ListAttachedRolePolicies",
"iam:ListPolicies",
"iam:GetPolicy",
"iam:GetPolicyVersion"
]
Resource = "*"
},
{
Sid = "DenyAdministratorAccess"
Effect = "Deny"
Action = ["iam:AttachRolePolicy", "iam:DetachRolePolicy"]
Resource = "arn:aws:iam::aws:policy/AdministratorAccess"
}
]
})
}
resource "aws_iam_role_policy_attachment" "ack_iam" {
role = aws_iam_role.ack_iam_controller.name
policy_arn = aws_iam_policy.ack_iam_controller.arn
}
resource "aws_eks_pod_identity_association" "ack_iam" {
cluster_name = module.eks.cluster_name
namespace = "ack-system"
service_account = "ack-iam-controller"
role_arn = aws_iam_role.ack_iam_controller.arn
}
Step 2. Install the IAM Controller via Helm
export SERVICE=iam
export RELEASE_VERSION=$(curl -sL https://api.github.com/repos/aws-controllers-k8s/${SERVICE}-controller/releases/latest | jq -r '.tag_name | ltrimstr("v")')
export ACK_SYSTEM_NAMESPACE=ack-system
export AWS_REGION=eu-west-1
# Authenticate with Public ECR
aws ecr-public get-login-password --region us-east-1 | helm registry login --username AWS --password-stdin public.ecr.aws
# Install Controller
helm install --create-namespace -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller \
oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart \
--version=$RELEASE_VERSION \
--set=aws.region=$AWS_REGION
Step 3. Create a Sample IAM Role Using Kubernetes YAML
sample-role.yaml
apiVersion: iam.services.k8s.aws/v1alpha1
kind: Role
metadata:
name: example-ack-role
spec:
name: example-ack-role
assumeRolePolicyDocument: |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}
description: "Managed by ACK inside Kubernetes"
maxSessionDuration: 3600
policies:
- arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
Step 4. Install S3 Controller and Provision S3 Bucket
Now install the S3 controller to manage Amazon S3 buckets via Kubernetes manifests:
export SERVICE=s3
export RELEASE_VERSION=$(curl -sL https://api.github.com/repos/aws-controllers-k8s/${SERVICE}-controller/releases/latest | jq -r '.tag_name | ltrimstr("v")')
helm install -n ack-system ack-$SERVICE-controller \
oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart \
--version=$RELEASE_VERSION \
--set=aws.region=eu-west-1
sample-bucket.yaml