There is a great article on the AWS Blog: How to use AWS Secrets Manager and ABAC for enhanced secrets management in Amazon EKS . The only downside of that approach is that it uses OpenID Connect (OIDC) and IRSA for authentication. But there is now a better and simpler option: Amazon EKS Pod Identity .

Using EKS Pod Identity, you can connect External Secrets Operator (ESO) with AWS Secrets Manager and still benefit from Attribute-Based Access Control (ABAC) for fine-grained permissions.


Prerequisites

Before you start, make sure you have:

  • An active AWS account
  • Permission to create IAM resources
  • Permission to manage KMS keys for Secrets Manager secrets
  • An existing EKS cluster
  • A user with permission to modify Kubernetes resources
  • AWS CLI, kubectl, helm, and eksctl installed
  • External Secrets Operator (ESO) installed

The setup requires two IAM roles:

  1. A role with access to Secrets Manager and KMS keys
  2. An EKS Pod Identity role for ESO

Follow the original AWS article until the section “Create an IAM role to access Secrets Manager secrets”, then continue here.


Create an IAM Role for the ESO Service Account

Let’s start by creating a role for the ESO service account.

Create a file named eso-policy.json:

{
  "Statement": [
    {
      "Action": ["sts:AssumeRole", "sts:TagSession"],
      "Effect": "Allow",
      "Resource": "arn:aws:iam::123456789012:role/external-secrets-abac",
      "Sid": "AssumeRole"
    }
  ],
  "Version": "2012-10-17"
}

This policy allows the ESO role to assume the external-secrets-abac role, which we will create later.

Next, create a trust policy for EKS Pod Identity. Save it as eso-trust-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "pods.eks.amazonaws.com"
      },
      "Action": ["sts:TagSession", "sts:AssumeRole"]
    }
  ]
}

Now create the ESO role and attach the policy:

aws iam create-policy --policy-name eso-policy --policy-document file://eso-policy.json
aws iam create-role --role-name external-secrets --assume-role-policy-document file://eso-trust-policy.json
aws iam attach-role-policy --role-name external-secrets --policy-arn arn:aws:iam::123456789012:policy/eso-policy

Create an IAM Role to Access Secrets Manager

Next, we will create a policy that grants ESO access to AWS Secrets Manager and KMS keys.

Create eso-abac-policy.json:

{
  "Statement": [
    {
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret",
        "secretsmanager:ListSecretVersionIds"
      ],
      "Condition": {
        "StringEquals": {
          "secretsmanager:ResourceTag/eks-secret-app": "${aws:PrincipalTag/eks-secret-app}"
        }
      },
      "Effect": "Allow",
      "Resource": "*",
      "Sid": "ExternalSecretsAccess"
    },
    {
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret",
        "secretsmanager:ListSecretVersionIds"
      ],
      "Condition": {
        "StringEquals": {
          "secretsmanager:ResourceTag/eks-secret-shared": "${aws:PrincipalTag/eks-secret-shared}"
        }
      },
      "Effect": "Allow",
      "Resource": "*",
      "Sid": "ExternalSecretsAccessShared"
    },
    {
      "Action": ["kms:Decrypt"],
      "Effect": "Allow",
      "Resource": ["<KMS Key ARN>"],
      "Sid": "ExternalSecretsKMSAccess"
    }
  ],
  "Version": "2012-10-17"
}

This policy conditionally allows access to secrets when aws:PrincipalTag values match the resource tags in Secrets Manager. Replace <KMS Key ARN> with your actual KMS key ARN. By default AWS Secrets Manager uses AWS managed aws/secretsmanager key.

Next, create a trust policy for the ABAC role: eso-abac-trust-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/external-secrets"
      },
      "Action": ["sts:AssumeRole", "sts:TagSession"],
      "Condition": {
        "ForAnyValue:StringEquals": {
          "aws:TagKeys": ["eks-secret-app", "eks-secret-shared"]
        }
      }
    }
  ]
}

This trust policy allows assuming the role only by arn:aws:iam::123456789012:role/external-secrets role and only if one of the following session tags are present.

Now create and attach the policy to the role:

aws iam create-policy --policy-name eso-abac-policy --policy-document file://eso-abac-policy.json
aws iam create-role --role-name external-secrets-abac --assume-role-policy-document file://eso-abac-trust-policy.json
aws iam attach-role-policy --role-name external-secrets-abac --policy-arn arn:aws:iam::123456789012:policy/eso-abac-policy

Create an EKS Pod Identity Association

This step links the ESO Kubernetes service account with the IAM role created above:

aws eks create-pod-identity-association \
  --cluster-name my-eks \
  --namespace external-secrets \
  --service-account external-secrets \
  --role-arn arn:aws:iam::123456789012:role/external-secrets

Create Secrets in AWS Secrets Manager

Now create two secrets in AWS Secrets Manager and tag them appropriately:

aws secretsmanager create-secret \
  --name MySecretOne \
  --secret-string '{"username":"user1","password":"pass1"}' \
  --tags Key=eks-secret-app,Value=my-secret-app

aws secretsmanager create-secret \
  --name MySecretTwo \
  --secret-string '{"apikey":"12345"}' \
  --tags Key=eks-secret-shared,Value=shared-secret-id

Verify that both secrets are created and tagged correctly:

aws secretsmanager list-secrets --query 'SecretList[*].{Name:Name, Tags:Tags}'

Test the Setup with External Secrets Operator

Create a namespace and deploy the following External Secrets Operator objects:

apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: aws-secrets-manager
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      role: arn:aws:iam::123456789012:role/external-secrets-abac
      sessionTags:
        - key: eks-secret-app
          value: my-secret-app
        - key: eks-secret-shared
          value: shared-secret-id
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: app-secret-one
spec:
  refreshInterval: 15s
  secretStoreRef:
    name: aws-secrets-manager
    kind: SecretStore
  target:
    name: app-secret-one
  data:
    - secretKey: MySecretOne
      remoteRef:
        key: MySecretOne
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: app-secret-two
spec:
  refreshInterval: 15s
  secretStoreRef:
    name: aws-secrets-manager
    kind: SecretStore
  target:
    name: app-secret-two
  data:
    - secretKey: MySecretTwo
      remoteRef:
        key: MySecretTwo

If everything is configured correctly, ESO will use the EKS Pod Identity to assume the role external-secrets, which then assumes the ABAC role external-secrets-abac with session tags eks-secret-app and eks-secret-shared. With these permissions, ESO will be able to synchronize the secrets directly into your Kubernetes cluster.

You can verify the secrets were created:

kubectl -n namespace get secrets
NAME               TYPE     DATA   AGE
app-secret-one     Opaque   1      1m
app-secret-two     Opaque   1      1m

Why Two Roles?

Two roles are required because we need to pass session tags from one role to another. While EKS Pod Identity automatically provides default tags, they do not include custom tags such as eks-secret-app or eks-secret-shared. Currently, Pod Identity agent verifies the identity of the caller (ESO) and hence it can not impersonate a different service account.

More details can be found in the ESO GitHub issue .

Default tags added by EKS Pod Identity:

"eks-cluster-name": "my-cluster",
"kubernetes-pod-uid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"eks-cluster-arn": "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster",
"kubernetes-pod-name": "external-secrets-xxxxxxxxxx-xxxxx",
"kubernetes-namespace": "external-secrets",
"kubernetes-service-account": "external-secrets"

Why Two Tags?

AWS Secrets Manager does not allow to have the same tag with different values for the same secret. But in some cases multiple applications need to access the same secret. For example, a shared API key for multiple applications. To solve this problem we can use eks-secret-shared tag. The value will be the same for all applications that need to access the same secret. In this case the secret will be accessible to all applications that have eks-secret-shared tag with the same value.

While eks-secret-app tag will be used to access secrets specific to the application. The value will be different for each application. In this case the secret will be accessible to the application that has eks-secret-app tag with the same value.


Conclusion

By combining EKS Pod Identity, AWS Secrets Manager ABAC, and External Secrets Operator, you get a modern, secure, and scalable approach to managing secrets in Kubernetes.

Key benefits of this approach:

  • Simplified authentication: EKS Pod Identity eliminates the need for OIDC providers and IRSA configuration
  • Fine-grained access control: ABAC uses tags to control which secrets each application can access
  • Reduced operational overhead: A single IAM role handles all secret access; adding new applications only requires updating tags
  • Lower risk of misconfiguration: No need to modify existing policies when onboarding new workloads
  • Flexible sharing: The dual-tag approach (eks-secret-app and eks-secret-shared) supports both app-specific and shared secrets

This setup is ideal for teams looking to automate secret synchronization between AWS and Kubernetes while maintaining strong security boundaries, all without the complexity of legacy IRSA-based solutions.