Skip to content

External Secrets Operator with AWS Secrets Manager

External Secrets EKS logo AWS Secrets Manager

External Secrets Operator (ESO) is a Kubernetes operator that synchronizes secrets from external APIs (such as AWS Secrets Manager, HashiCorp Vault, and GCP Secret Manager) into native Kubernetes Secret objects.

This guide demonstrates how to configure ESO on Amazon EKS using EKS Pod Identity and ClusterSecretStores.


Architecture

graph LR
    AWS["AWS Secrets Manager<br/>(e.g. eks/mysecret)"] --> ESO["External Secrets Operator Pod"]
    ESO --> CSS["ClusterSecretStore (eks-secretstore)"]
    CSS --> ES["ExternalSecret CRD"]
    ES --> K8sSecret["Native Kubernetes Secret<br/>(mysecretink8s)"]
    K8sSecret --> Pod["Application Pod"]

Step 1. IAM Policy & EKS Pod Identity

eso-iam.tf

# 1. IAM Policy scoped to path 'eks/*'
resource "aws_iam_policy" "eso_secrets_policy" {
  name        = "ESOSecretsManagerPolicy-${module.eks.cluster_name}"
  description = "Allows External Secrets Operator to read secrets under path eks/*"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "secretsmanager:GetResourcePolicy",
          "secretsmanager:GetSecretValue",
          "secretsmanager:DescribeSecret",
          "secretsmanager:ListSecretVersionIds",
          "secretsmanager:ListSecrets",
          "secretsmanager:BatchGetSecretValue"
        ]
        Resource = ["arn:aws:secretsmanager:eu-west-1:*:secret:eks/*"]
      }
    ]
  })
}

# 2. IAM Role with Pod Identity Trust
data "aws_iam_policy_document" "pod_id_trust" {
  statement {
    effect = "Allow"
    principals {
      type        = "Service"
      identifiers = ["pods.eks.amazonaws.com"]
    }
    actions = ["sts:AssumeRole", "sts:TagSession"]
  }
}

resource "aws_iam_role" "eso_controller" {
  name               = "external-secrets-role-${module.eks.cluster_name}"
  assume_role_policy = data.aws_iam_policy_document.pod_id_trust.json
}

resource "aws_iam_role_policy_attachment" "eso_attach" {
  role       = aws_iam_role.eso_controller.name
  policy_arn = aws_iam_policy.eso_secrets_policy.arn
}

# 3. Pod Identity Association
resource "aws_eks_pod_identity_association" "eso" {
  cluster_name    = module.eks.cluster_name
  namespace       = "external-secrets"
  service_account = "external-secrets"
  role_arn        = aws_iam_role.eso_controller.arn
}


Step 2. Install External Secrets Operator via Helm

helm repo add external-secrets https://charts.external-secrets.io
helm repo update

helm install external-secrets external-secrets/external-secrets \
  --namespace external-secrets \
  --create-namespace \
  --version 0.12.1

Step 3. Create ClusterSecretStore

cluster-secret-store.yaml

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: eks-secretstore
spec:
  provider:
    aws:
      service: SecretsManager
      region: eu-west-1
  conditions:
    - namespaces:
        - default
        - cert-manager

Authentication via Pod Identity

When no explicit authentication credentials or roles are provided in spec.provider.aws, ESO uses the EKS Pod Identity credentials injected into the operator pod automatically.


Step 4. Create ExternalSecret to Inject Kubernetes Secrets

external-secret.yaml

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: mysecretfromsecretsmanager
  namespace: default
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: eks-secretstore
    kind: ClusterSecretStore
  target:
    name: mysecretink8s
    creationPolicy: Owner
  data:
    - secretKey: username
      remoteRef:
        key: eks/mysecret
        property: user
    - secretKey: password
      remoteRef:
        key: eks/mysecret
        property: password

# 1. Apply ExternalSecret
kubectl apply -f external-secret.yaml

# 2. Check synchronization status
kubectl get externalsecret mysecretfromsecretsmanager

# 3. Verify generated native Kubernetes secret
kubectl get secret mysecretink8s -o yaml