Skip to content

AWS Controllers for Kubernetes (ACK) Getting Started

EKS Auto mode ACK (AWS Controller for Kubernetes) Terraform logo

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

kubectl apply -f sample-role.yaml
kubectl describe role.iam.services.k8s.aws example-ack-role

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

apiVersion: s3.services.k8s.aws/v1alpha1
kind: Bucket
metadata:
  name: ack-demo-bucket-2026
spec:
  name: ack-demo-bucket-2026
  versioning:
    status: Enabled
  tagging:
    tagSet:
      - key: Environment
        value: Dev
      - key: ManagedBy
        value: ACK

kubectl apply -f sample-bucket.yaml
kubectl get bucket.s3.services.k8s.aws