AWS
This integration guide is for accepting workload identity that has already been published through oidc.pub. Configure AWS IAM to trust the oidc.pub discovery endpoint as an OpenID Connect identity provider, then allow workloads to assume IAM roles using sts:AssumeRoleWithWebIdentity without static credentials.
AWS: Creating OpenID Connect identity providers
1. Create the OIDC identity provider
Register your oidc.pub subdomain as an OpenID Connect identity provider in AWS IAM. Replace myservice.oidc.pub with your actual subdomain.
# Terraform
resource "aws_iam_openid_connect_provider" "oidcpub" {
url = "https://myservice.oidc.pub"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = [] # AWS fetches this automatically for public CAs
}# Terraform
resource "aws_iam_openid_connect_provider" "oidcpub" {
url = "https://myservice.oidc.pub"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = [] # AWS fetches this automatically for public CAs
}The client_id_list must include the aud claim that your tokens carry. Most setups use sts.amazonaws.com, but the value depends on the provider — with GitLab CI, for instance, you set the audience explicitly in the job's id_tokens config (commonly the issuer URL itself).
2. Create the IAM role
Create an IAM role with a trust policy that allows sts:AssumeRoleWithWebIdentity from your OIDC provider. The Condition block restricts which identities can assume the role by matching the sub and aud claims in the token.
# Terraform
resource "aws_iam_role" "workload_role" {
name = "oidcpub-workload-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.oidcpub.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"myservice.oidc.pub:aud" = "sts.amazonaws.com"
"myservice.oidc.pub:sub" = "your-subject-claim"
}
}
}]
})
}# Terraform
resource "aws_iam_role" "workload_role" {
name = "oidcpub-workload-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.oidcpub.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"myservice.oidc.pub:aud" = "sts.amazonaws.com"
"myservice.oidc.pub:sub" = "your-subject-claim"
}
}
}]
})
}The sub condition depends on your OIDC provider — see the provider-specific examples below for the correct values.
Limited claim support: AWS IAM trust policies can only match sub, aud, and amr claims from the OIDC token — custom claims are not supported as condition keys. You can use StringLike with wildcards (*, ?) for pattern matching on these claims. AWS: Available keys for IAM web identity federation
3. Assume the role
From your workload, call sts:AssumeRoleWithWebIdentity with your OIDC token. Most AWS SDKs handle this automatically when AWS_WEB_IDENTITY_TOKEN_FILE and AWS_ROLE_ARN are set.
aws sts assume-role-with-web-identity \
--role-arn arn:aws:iam::123456789012:role/oidcpub-workload-role \
--role-session-name my-session \
--web-identity-token "$OIDC_TOKEN"aws sts assume-role-with-web-identity \
--role-arn arn:aws:iam::123456789012:role/oidcpub-workload-role \
--role-session-name my-session \
--web-identity-token "$OIDC_TOKEN"Provider-specific examples
Each OIDC provider uses different sub and aud claims. Use the examples below to adapt the general setup to your provider.
Kubernetes (IRSA)
Kubernetes service account tokens use sts.amazonaws.com as the audience and the service account identifier as the subject. See the Kubernetes guide for full setup instructions.
// Trust policy condition
"Condition": {
"StringEquals": {
"k8s-prod.oidc.pub:aud": "sts.amazonaws.com",
"k8s-prod.oidc.pub:sub": "system:serviceaccount:default:my-app"
}
}// Trust policy condition
"Condition": {
"StringEquals": {
"k8s-prod.oidc.pub:aud": "sts.amazonaws.com",
"k8s-prod.oidc.pub:sub": "system:serviceaccount:default:my-app"
}
}Add a projected volume to your Pod spec with the correct audience, and set the AWS_WEB_IDENTITY_TOKEN_FILE and AWS_ROLE_ARN environment variables so the AWS SDK picks them up automatically.
# Pod spec
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
serviceAccountName: my-app
containers:
- name: app
image: my-app:latest
env:
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: /var/run/secrets/oidc/token
- name: AWS_ROLE_ARN
value: arn:aws:iam::123456789012:role/k8s-pod-role
volumeMounts:
- name: oidc-token
mountPath: /var/run/secrets/oidc
readOnly: true
volumes:
- name: oidc-token
projected:
sources:
- serviceAccountToken:
audience: sts.amazonaws.com
expirationSeconds: 3600
path: token# Pod spec
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
serviceAccountName: my-app
containers:
- name: app
image: my-app:latest
env:
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: /var/run/secrets/oidc/token
- name: AWS_ROLE_ARN
value: arn:aws:iam::123456789012:role/k8s-pod-role
volumeMounts:
- name: oidc-token
mountPath: /var/run/secrets/oidc
readOnly: true
volumes:
- name: oidc-token
projected:
sources:
- serviceAccountToken:
audience: sts.amazonaws.com
expirationSeconds: 3600
path: tokenThe amazon-eks-pod-identity-webhook watches for ServiceAccounts with the eks.amazonaws.com/role-arn annotation and automatically injects the projected token volume, mount, and environment variables.
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: default
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/k8s-pod-role# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: default
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/k8s-pod-roleThe webhook works on any Kubernetes cluster — it is not limited to EKS.
HashiCorp Vault
Vault's OIDC identity tokens do not include an sts.amazonaws.com audience by default — you need to configure it on the OIDC role. See the Vault guide for full setup instructions.
# Vault OIDC role
vault write identity/oidc/role/aws-workload \
key=my-key \
client_id="sts.amazonaws.com" \
ttl=1h# Vault OIDC role
vault write identity/oidc/role/aws-workload \
key=my-key \
client_id="sts.amazonaws.com" \
ttl=1h# Obtain a token
OIDC_TOKEN=$(vault read -field=token identity/oidc/token/aws-workload)# Obtain a token
OIDC_TOKEN=$(vault read -field=token identity/oidc/token/aws-workload)// Trust policy condition
"Condition": {
"StringEquals": {
"vault.oidc.pub:aud": "sts.amazonaws.com",
"vault.oidc.pub:sub": "vault-entity-id"
}
}// Trust policy condition
"Condition": {
"StringEquals": {
"vault.oidc.pub:aud": "sts.amazonaws.com",
"vault.oidc.pub:sub": "vault-entity-id"
}
}GitLab CI
GitLab CI ID tokens carry whatever audience you set in the job's id_tokens configuration — it must be specified explicitly (here, the issuer URL). The subject encodes the project path and ref. See the GitLab CI guide for full setup instructions.
// Trust policy condition
"Condition": {
"StringEquals": {
"gitlab.oidc.pub:aud": "https://gitlab.oidc.pub"
},
"StringLike": {
"gitlab.oidc.pub:sub": "project_path:mygroup/myproject:ref_type:branch:ref:main"
}
}// Trust policy condition
"Condition": {
"StringEquals": {
"gitlab.oidc.pub:aud": "https://gitlab.oidc.pub"
},
"StringLike": {
"gitlab.oidc.pub:sub": "project_path:mygroup/myproject:ref_type:branch:ref:main"
}
}# .gitlab-ci.yml
deploy:
id_tokens:
OIDC_TOKEN:
aud: https://gitlab.oidc.pub
script:
- >
CREDENTIALS=$(aws sts assume-role-with-web-identity
--role-arn arn:aws:iam::123456789012:role/gitlab-deploy
--role-session-name gitlab-ci
--web-identity-token "$OIDC_TOKEN")
- export AWS_ACCESS_KEY_ID=$(echo $CREDENTIALS | jq -r .Credentials.AccessKeyId)
- export AWS_SECRET_ACCESS_KEY=$(echo $CREDENTIALS | jq -r .Credentials.SecretAccessKey)
- export AWS_SESSION_TOKEN=$(echo $CREDENTIALS | jq -r .Credentials.SessionToken)
- aws s3 ls# .gitlab-ci.yml
deploy:
id_tokens:
OIDC_TOKEN:
aud: https://gitlab.oidc.pub
script:
- >
CREDENTIALS=$(aws sts assume-role-with-web-identity
--role-arn arn:aws:iam::123456789012:role/gitlab-deploy
--role-session-name gitlab-ci
--web-identity-token "$OIDC_TOKEN")
- export AWS_ACCESS_KEY_ID=$(echo $CREDENTIALS | jq -r .Credentials.AccessKeyId)
- export AWS_SECRET_ACCESS_KEY=$(echo $CREDENTIALS | jq -r .Credentials.SecretAccessKey)
- export AWS_SESSION_TOKEN=$(echo $CREDENTIALS | jq -r .Credentials.SessionToken)
- aws s3 lsGitLab: Configure OpenID Connect in AWS
Terraform Enterprise
Terraform Enterprise workload identity tokens use aws.workload.identity as the audience for AWS dynamic credentials. The subject encodes the organization, project, workspace, and run phase. If you expose OIDC through an oidc.pub secondary hostname, use that hostname in the IAM condition keys. See the Terraform Enterprise guide for the publishing setup.
// Trust policy condition
"Condition": {
"StringEquals": {
"tfe.oidc.pub:aud": "aws.workload.identity",
"tfe.oidc.pub:sub": "organization:my-org:project:Default Project:workspace:networking:run_phase:apply"
}
}// Trust policy condition
"Condition": {
"StringEquals": {
"tfe.oidc.pub:aud": "aws.workload.identity",
"tfe.oidc.pub:sub": "organization:my-org:project:Default Project:workspace:networking:run_phase:apply"
}
}