# How do I deploy from GitHub Actions to the cloud without long lived secrets?

Source: https://gitshowcase.com/forum/discuss/how-do-i-deploy-from-github-actions-to-the-cloud-without-long-lived/
Site: GitShowcase Forum, category CI/CD and GitHub Actions
Published: 2026-11-15. Updated: 2026-11-15. Replies: 0.

## Question by Maren Castillo (staff), 2026-11-15

Use OpenID Connect (OIDC). Your cloud provider trusts GitHub as an identity provider, each job asks GitHub for a signed token, and the cloud swaps it for credentials that expire with the job. You add `id-token: write` to the workflow and lock the cloud role to your repository and branch or environment.

## Why this replaces stored keys

The old pattern is to create an access key in your cloud account, paste it into a GitHub secret, and present it on every run. That key lives until someone rotates it, and anyone who can read the secret can use it from anywhere. According to the [GitHub Actions OIDC docs](https://docs.github.com/en/actions/concepts/security/openid-connect), OIDC removes the duplicated cloud secret: you set up a trust relationship once, and the provider issues a short-lived token that is only valid for a single job and then expires on its own.

The flow, in four steps:

1. You tell the cloud that tokens from GitHub's issuer are acceptable, and for which workflows.
2. Each job can request a JSON web token (JWT) from GitHub. It carries claims such as the repository, the branch or environment, and the workflow.
3. The workflow presents that token to the cloud.
4. The cloud checks the claims against the trust rules you wrote and returns temporary credentials.

## Check first

- Is the job running on a GitHub-hosted or self-hosted runner that can reach both GitHub's token endpoint and your cloud's token service?
- Is the job triggered by a pull request? Its token claims differ from a push to `main`, so a trust policy written for `main` will not match.
- Which cloud? The steps below use AWS because GitHub documents it step by step. Azure, Google Cloud, HashiCorp Vault and others have their own pages in the same docs section, and the shape is the same.

## Step by step on AWS

**1. Add GitHub as an identity provider in IAM.** Per the [GitHub guide for AWS](https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-aws), the provider URL is `https://token.actions.githubusercontent.com`, and the audience is `sts.amazonaws.com` if you use the official `aws-actions/configure-aws-credentials` action.

**2. Create a role with a trust policy that names your repo.** The docs say you must define at least one condition so that untrusted repositories cannot request tokens for your resources. Match on the `sub` claim:

```json
{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
  },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
      "token.actions.githubusercontent.com:sub": "repo:<OWNER>/<REPO>:environment:prod"
    }
  }
}
```

**3. Give the role only the permissions the deploy needs.** A role that can write one bucket or update one service is far safer than an admin role. The trust policy decides who can get in; the permission policy decides what they can do.

**4. Update the workflow.** Two changes: the `permissions` block and the credentials step.

```yaml
name: deploy
on:
  push:
    branches: [main]

permissions:
  id-token: write   # lets the job request the OIDC token
  contents: read    # lets checkout read the repo

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: prod
    steps:
      - uses: actions/checkout@v6
      - uses: aws-actions/configure-aws-credentials@<PINNED_COMMIT_SHA>
        with:
          role-to-assume: arn:aws:iam::<ACCOUNT_ID>:role/<DEPLOY_ROLE>
          aws-region: <REGION>
      - run: aws s3 sync ./dist s3://<BUCKET_NAME>/
```

Pin third party actions to a full commit SHA where you can, as GitHub's own example does. `id-token: write` does not let the workflow write to your repo; the docs say it only allows requesting and using the OIDC token.

## Match the `sub` claim you actually have

Most failures are a mismatch between the token's `sub` and the trust policy. The common shapes, as of the GitHub docs in November 2026:

| Workflow context | `sub` looks like |
| --- | --- |
| Branch push | `repo:<OWNER>/<REPO>:ref:refs/heads/<BRANCH>` |
| Job with an environment | `repo:<OWNER>/<REPO>:environment:<ENV>` |
| Any branch or environment in one repo | `StringLike` with `repo:<OWNER>/<REPO>:*` |

The docs also note a newer format: repositories created after July 15, 2026, or that opt in, include immutable owner and repository IDs in `sub`, written like `repo:<OWNER>@<ID>/<REPO>@<ID>:...`. If your role worked for old repos and fails for a new one, print the claim and compare. Also remember that when a job uses an environment, `sub` names the environment, not the branch.

## Add protection on the environment

When you key trust to an environment, add protection rules on that environment in GitHub, such as restricting which branches can deploy to it. GitHub's AWS page recommends this for extra security. Without it, anyone who can push a workflow that targets the environment can request the same token.

## Common errors

- **"Credentials could not be loaded" or "Not authorized to perform sts:AssumeRoleWithWebIdentity":** the `sub` or `aud` in the trust policy does not match the token. Check both.
- **"Unable to get ACTIONS_ID_TOKEN_REQUEST_URL":** the job is missing `id-token: write`. Set it at the workflow or job level.
- **Works on `main`, fails on a pull request:** your trust policy names a branch or environment the PR job does not have.

If your cache step is also misbehaving in the same workflow, see [why a GitHub Actions cache never restores](/forum/discuss/why-is-my-github-actions-cache-never-restoring-between-runs/). For platform comparisons, the [CI/CD guide](https://gitshowcase.com/best/cicd/) covers the options.

---
Cite as: GitShowcase Forum, "How do I deploy from GitHub Actions to the cloud without long lived secrets?", https://gitshowcase.com/forum/discuss/how-do-i-deploy-from-github-actions-to-the-cloud-without-long-lived/, accessed 2026-11-15.
