Join our Newsletter — 33% off our NHI Course
Home› Guides› How to federate Bitbucket Pipelines to AWS: Swap…
Guide Cloud & Workload Identity

How to federate Bitbucket Pipelines to AWS: Swap stored AWS keys for OIDC role credentials

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 11 November 2026 12 min read
On this page

Bitbucket Pipelines repositories commonly hold AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY as secured repository or workspace variables, which are long-lived keys available to every step that can read them. Bitbucket Cloud can instead issue an OpenID Connect token to each step, which AWS STS exchanges for temporary credentials for a role whose trust policy names the workspace, the repository and, ideally, the deployment environment. This page is the runbook: collect the identifiers, create the provider and role, update bitbucket-pipelines.yml, test, roll back and delete the keys. It is for DevOps and platform engineers who administer Bitbucket workspaces and AWS accounts. For the wider platform hardening picture see the Bitbucket Security Guide; this page focuses on the migration.

This is an independent guide from NHI Mgmt Group, based on Atlassian's and AWS's public documentation as checked in November 2026. It is not sponsored or reviewed by Atlassian or AWS. Confirm settings against the current vendor documentation before changing production.

Key takeaways

  • Without a sub condition, the whole workspace is trusted. Atlassian states that by default any repository in the workspace can assume the role, and so can anyone holding a token. The issuer and audience are per workspace, so they do not narrow anything inside it.
  • The subject is built from UUIDs, not names. It takes the form {REPOSITORY_UUID}[:{ENVIRONMENT_UUID}]:{STEP_UUID}. Renaming a repository does not break or transfer the trust, which is safer than name-based subjects.
  • Bind production roles to a deployment environment. The environment UUID only appears in the subject when the step is a deployment step, so a trust policy that requires it cannot be met by an ordinary branch or pull request build.
  • The token is a file you write yourself. The step exposes BITBUCKET_STEP_OIDC_TOKEN; you write it to the path in AWS_WEB_IDENTITY_TOKEN_FILE and set AWS_ROLE_ARN. Never echo it into logs or save it as an artifact.
  • Finish by deleting keys. The migration is done only when the IAM user's keys are deleted and the secured variables removed at repository, deployment and workspace level.

When you need this

  • AWS keys sit in secured variables. Workspace-level variables are the widest exposure: every repository in the workspace receives them. Secured variables are masked in logs, but any step can still use or exfiltrate them. A researcher's demonstration of a Bitbucket pipeline leading to server takeover shows how pipeline configuration and secrets become an attack path.
  • One IAM user serves many repositories. CloudTrail cannot tell which repository made a call. Federated sessions record the repository and step UUIDs.
  • You are already reviewing Atlassian credentials. If you are moving off app passwords, do this at the same time. See Atlassian App Passwords to API Tokens.

Before you start

ItemWhere to find itExample form
Identity provider URLRepository settings, Pipelines, OpenID Connecthttps://api.bitbucket.org/2.0/workspaces/<WORKSPACE>/pipelines-config/identity/oidc
AudienceSame settings pageari:cloud:bitbucket::workspace/<WORKSPACE_UUID>
Repository UUIDSame settings page, or $BITBUCKET_REPO_UUID in any build{xxxxxxxx-...}
Deployment environment UUIDSame settings page, or $BITBUCKET_DEPLOYMENT_ENVIRONMENT_UUID in a deployment step{xxxxxxxx-...}
AWS permissionsIAMRights to create an OIDC identity provider and roles
Bitbucket permissionsWorkspace or repository adminTo manage variables, deployment environments and their restrictions
Permissions planCloudTrail history of the existing IAM userThe exact actions and resources each pipeline uses

Bitbucket shows UUIDs wrapped in curly braces. Copy the identifiers exactly as the OpenID Connect settings page shows them, including the braces, and confirm the final subject from a real token or CloudTrail event before tightening the policy.

Choosing the subject condition

Scopesub condition (StringLike)Use it forWatch out for
WorkspaceNo sub conditionNever for write rolesEvery repository, present and future, can assume the role
One repository{REPO_UUID}:*Read-only or test rolesEvery branch and pull request build in that repository qualifies
One repository, one environment{REPO_UUID}:{ENV_UUID}:*Production and staging deploy rolesOnly as strong as the environment's deployment restrictions

Atlassian's own example for deployment steps uses {REPO_UUID}*:{ENVIRONMENT_UUID}:*. The step UUID changes every run, so a wildcard on the final segment is unavoidable; that is why the environment segment, not the step, carries the production boundary.

Step-by-step

  1. Inventory first (read-only). List repositories, deployment environments and the workspace with AWS variables, and record which IAM user each key belongs to. In AWS, check for an existing provider with aws iam list-open-id-connect-providers; there is one provider per URL per account, and one Bitbucket provider covers the whole workspace.
  2. Create the OIDC identity provider.
    aws iam create-open-id-connect-provider \
      --url https://api.bitbucket.org/2.0/workspaces/<WORKSPACE>/pipelines-config/identity/oidc \
      --client-id-list ari:cloud:bitbucket::workspace/<WORKSPACE_UUID>
    AWS verifies the issuer certificate against its trusted root CA library and uses a thumbprint only as a fallback. Atlassian's AWS page publishes a thumbprint for manual setups; you do not normally need to supply one.
  3. Write the trust policy for a production environment. Save as trust.json:
    {
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Principal": {"Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/api.bitbucket.org/2.0/workspaces/<WORKSPACE>/pipelines-config/identity/oidc"},
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
          "StringEquals": {
            "api.bitbucket.org/2.0/workspaces/<WORKSPACE>/pipelines-config/identity/oidc:aud": "ari:cloud:bitbucket::workspace/<WORKSPACE_UUID>"
          },
          "StringLike": {
            "api.bitbucket.org/2.0/workspaces/<WORKSPACE>/pipelines-config/identity/oidc:sub": "{<REPO_UUID>}:{<ENV_UUID>}:*"
          }
        }
      }]
    }
    The condition key prefix is the provider URL without https://, path included. AWS maps generic OIDC providers to aud, sub, amr, email and oaud only, so repository and environment scoping must live in sub.
  4. Create the role and attach permissions.
    aws iam create-role --role-name bbp-payments-api-prod-deploy \
      --assume-role-policy-document file://trust.json --max-session-duration 3600
    aws iam put-role-policy --role-name bbp-payments-api-prod-deploy \
      --policy-name deploy --policy-document file://deploy-permissions.json
    Build the permissions from what the pipeline actually calls. See the AWS IAM Policies and Roles Guide.
  5. Restrict the deployment environment in Bitbucket. Limit which branches can deploy to the environment and, where your plan allows it, which users can run deployments. Bitbucket only releases an environment's variables to a step when the branch and admin permission checks pass, and manual steps can only be triggered by users with write access.
  6. Update bitbucket-pipelines.yml.
    pipelines:
      branches:
        main:
          - step:
              name: Deploy to production
              deployment: production
              oidc: true
              image: amazon/aws-cli
              script:
                - export AWS_REGION=eu-west-2
                - export AWS_ROLE_ARN=arn:aws:iam::<ACCOUNT_ID>:role/bbp-payments-api-prod-deploy
                - export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
                - echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
                - aws sts get-caller-identity
    The AWS CLI and SDKs read AWS_ROLE_ARN and AWS_WEB_IDENTITY_TOKEN_FILE and call AssumeRoleWithWebIdentity themselves. Atlassian's documentation is inconsistent about whether OIDC is on by default, so set oidc: true on steps that need it and oidc: false on steps that do not, such as pull request builds.
  7. Pilot on staging. Create a staging role bound to the staging environment UUID and run it first. Keep the old variables in place but unused by the federated step until both environments deploy cleanly.

Verify it worked

  • Step output: aws sts get-caller-identity returns an assumed-role ARN for the new role, not an IAM user.
  • CloudTrail: find AssumeRoleWithWebIdentity with userIdentity.type WebIdentityUser. responseElements.subjectFromWebIdentityToken shows the repository, environment and step UUIDs, and responseElements.audience the workspace audience. Global STS endpoint calls are logged in us-east-1.
  • Negative tests: run the same step from a feature branch without deployment: production, and from another repository in the workspace. Both must be denied.
  • Token hygiene: confirm the token file is not listed under artifacts and that no script prints it.
CloudTrail fieldWhat it tells you for Bitbucket
userIdentity.identityProviderThe workspace's Bitbucket provider ARN
responseElements.subjectFromWebIdentityTokenRepository UUID, environment UUID (deployment steps) and step UUID
requestParameters.roleSessionNameSession name chosen by the CLI or SDK; match on the step UUID from the subject instead
sourceIPAddressEgress address of the build, useful for anomaly review
additionalEventData.identityProviderConnectionVerificationMethodIAMTrustStore or Thumbprint

Rollback and common errors

SymptomLikely causeFix
"Not authorized to perform sts:AssumeRoleWithWebIdentity"Subject mismatch: braces missing, environment UUID from another repository, or step not a deployment stepCompare subjectFromWebIdentityToken from the failed event with the policy pattern
BITBUCKET_STEP_OIDC_TOKEN is emptyOIDC not enabled for that stepAdd oidc: true to the step
Audience or provider errorProvider created with a different URL or client ID, or values copied from another workspaceRecreate the provider with the values from the OpenID Connect settings page
CLI still uses old keysStatic AWS_ACCESS_KEY_ID variables still present take precedence in the credential chainRemove or rename the static variables for that environment

Rollback: during the pilot, revert the YAML change to restore key-based login; nothing in AWS needs undoing. After cut-over, deactivate the old keys (aws iam update-access-key --status Inactive), wait a full release cycle, then delete the keys and the secured variables at every level. See How to rotate an AWS IAM access key without downtime. To withdraw access, edit or delete the role's trust policy; sessions already issued last until they expire.

Decisions: environments, runners and IP conditions

DecisionOption AOption BGuidance
Role scopeOne role per repositoryOne role per repository and environmentUse B for anything that writes to production; A is acceptable for read-only checks
Build infrastructureAtlassian-hosted stepsSelf-hosted runners (runs-on)OIDC works on both; on self-hosted runners, a non-ephemeral host can retain token files, so clean the workspace after each step
Source IP conditionNoneaws:SourceIp on the trust policyAtlassian does not recommend IP ranges as a security control: standard 1x and 2x steps run from broad AWS ranges, and a smaller fixed set needs atlassian-ip-ranges: true on 4x or larger steps. Treat IP conditions as defence in depth only
AccountsOne role in a shared deploy accountOne role per target accountPer-account roles keep blast radius small; small teams may start with one account and split later

Small organisations with a handful of repositories can manage roles by hand. Larger ones should create provider and roles from a dedicated infrastructure pipeline whose own role is not trusted by ordinary repositories, so a compromised repository cannot rewrite the trust that governs it. The same pattern for other platforms is in How to set up workload identity federation for GitLab CI to Azure.

Hardening and monitoring afterwards

  • Disable OIDC where unused: set oidc: false on pull request and fork-triggered steps.
  • Protect the environments: branch restrictions on production deployment environments are part of the trust boundary.
  • Alert on drift: UpdateAssumeRolePolicy on bbp-* roles, CreateOpenIDConnectProvider outside change windows, and AssumeRoleWithWebIdentity whose subject contains a repository UUID not on your list.
  • Remove workspace-level AWS variables: they are inherited by every repository.
MetricTarget
Secured variables holding AWS secret keys (all levels)Zero
Bitbucket-trusted roles without a sub conditionZero
Production deploy roles bound to an environment UUIDAll
Federated sessions from unknown repository UUIDsZero; investigate every one
Trust policy changes without a change recordZero

Migrating a whole workspace

  1. Rank repositories by exposure. Start with repositories whose keys reach production or hold IAM write rights, then shared workspace-level keys, then the long tail of read-only keys.
  2. Create the provider once per account. Because the issuer and audience are per workspace, one provider in each AWS account serves every repository; only roles are per repository and environment.
  3. Template the roles. Generate trust policies from a list of repository and environment UUIDs held in code, so every new repository gets a reviewed role rather than a copied key.
  4. Track the old IAM users. For each user, record the repositories that used it and its key last-used date. A user is ready for deletion only when every consumer has moved and the key has shown no use for a full release cycle.
  5. Close the door. Once migration is complete, use an SCP or a detective control to flag any new IAM user access key created for CI, so the old pattern does not creep back. See How to find unused AWS IAM roles and access keys.

Framework mapping

FrameworkReferenceRelevance
MITRE ATT&CKT1552.001 Credentials In Files; T1078.004 Cloud Accounts; T1098.003 Additional Cloud RolesExposed static keys; abuse of valid cloud roles; tampering with role trust
NIST SP 800-53 Rev. 5IA-5, IA-5(7), AC-6, AU-6Authenticator management, no embedded static authenticators, least privilege, log review
NIST CSF 2.0PR.AA-01, PR.AA-05, DE.CM-03Credential management, least privilege, monitoring
OWASP NHI Top 10 2025NHI6 Insecure Cloud Deployment Configurations; NHI7 Long-Lived SecretsStatic CI keys and loose OIDC conditions

Practitioner checklist

  • List every repository, deployment and workspace variable that holds AWS keys.
  • Create one Bitbucket OIDC provider per workspace in each target AWS account.
  • Pin aud with StringEquals and always include a sub condition.
  • Bind production roles to repository UUID plus deployment environment UUID.
  • Restrict which branches can deploy to production environments.
  • Set oidc: true only on steps that need AWS access.
  • Run positive and negative tests and check the subject in CloudTrail.
  • Deactivate, then delete, old keys and secured variables at every level.

Standards and references

Related NHI Mgmt Group resources: CI/CD Pipeline Identity Security Guide · How to set up workload identity federation for GitHub Actions to AWS · How to federate Terraform Cloud to AWS · How to Audit CI/CD Secrets Scope · Multi-Cloud Workload Identity Troubleshooting Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 11 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org