Como funcionaHow it works
Em vez de guardar chaves de acesso da AWS nos secrets do GitHub, o GitHub Actions emite um token OIDC de curta duração para cada execução. A AWS confia no GitHub como provedor de identidade e troca esse token por credenciais temporárias de uma role IAM.Instead of storing AWS access keys in GitHub secrets, GitHub Actions issues a short-lived OIDC token for each run. AWS trusts GitHub as an identity provider and exchanges that token for temporary credentials of an IAM role.
- O job pede um token ao GitHub (precisa de
id-token: write).The job asks GitHub for a token (it needsid-token: write). - A action
aws-actions/configure-aws-credentialsenvia o token à AWS (sts:AssumeRoleWithWebIdentity).Theaws-actions/configure-aws-credentialsaction sends the token to AWS (sts:AssumeRoleWithWebIdentity). - A AWS confere a trust policy da role: emissor, audiência (
aud) e quem está pedindo (sub).AWS checks the role’s trust policy: issuer, audience (aud) and who is asking (sub). - Se bater, devolve credenciais temporárias (por padrão, 1 hora) que os passos seguintes usam.If it matches, AWS returns temporary credentials (1 hour by default) that the following steps use.
Não há segredo para vazar nem para rotacionar. O controle de quem pode fazer deploy passa a morar na trust policy.There is no secret to leak or rotate. Control over who can deploy now lives in the trust policy.
O desenhoThe layout
- Branch
develop→ ambientedevelopna AWS.Branchdevelop→developenvironment on AWS. - Branch
main→ ambienteproductionna AWS.Branchmain→productionenvironment on AWS. - Um GitHub Environment por ambiente, cada um com sua role IAM e suas variáveis.One GitHub Environment per environment, each with its own IAM role and variables.
O exemplo faz deploy de uma imagem Docker no Amazon ECR e atualiza um serviço do Amazon ECS, o mesmo caminho das notas de AWS. Se o seu destino for outro (S3, EC2, Lambda), as etapas 1 a 3 não mudam; só as permissões da role e os passos de deploy do workflow.The example deploys a Docker image to Amazon ECR and updates an Amazon ECS service, the same path as the AWS notes. If your target is something else (S3, EC2, Lambda), steps 1 to 3 stay the same; only the role’s permissions and the workflow’s deploy steps change.
Se develop e produção ficam em contas AWS diferentes, repita as etapas 1 e 2 em cada conta: o provedor OIDC usado na trust policy precisa estar na mesma conta da role.If develop and production live in different AWS accounts, repeat steps 1 and 2 in each account: the OIDC provider used in a trust policy must be in the same account as the role.
1. Provedor OIDC na AWS1. OIDC provider in AWS
Faça uma vez por conta: IAM → Identity providers → Add provider → OpenID Connect.Do it once per account: IAM → Identity providers → Add provider → OpenID Connect.
- Provider URL ·
https://token.actions.githubusercontent.com - Audience ·
sts.amazonaws.com
Pela CLI, o comando é o abaixo (confira na referência da CLI se ele pede a lista de thumbprints na sua versão).From the CLI, the command is the one below (check the CLI reference for whether your version asks for a thumbprint list).
aws iam create-open-id-connect-provider --url https://token.actions.githubusercontent.com --client-id-list sts.amazonaws.com
Sobre thumbprints: a AWS valida o certificado do endpoint do provedor com a sua biblioteca de autoridades raiz confiáveis, e só recorre aos thumbprints cadastrados se o certificado não for assinado por uma delas.About thumbprints: AWS verifies the provider endpoint’s certificate with its library of trusted root CAs, and only falls back to the configured thumbprints if the certificate is not signed by one of them.
2. Roles IAM e trust policy2. IAM roles and trust policy
Crie uma role por ambiente (por exemplo github-deploy-develop e github-deploy-production). A trust policy diz quem pode assumir a role. A condição em sub é obrigatória: sem ela, qualquer repositório do GitHub poderia pedir acesso.Create one role per environment (for example github-deploy-develop and github-deploy-production). The trust policy says who may assume the role. The sub condition is required: without it, any repository on GitHub could ask for access.
{
"Version": "2012-10-17",
"Statement": [
{
"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:<ORG>/<REPO>:environment:develop"
}
}
}
]
}
Na role de produção, troque o final do sub por environment:production.On the production role, change the end of sub to environment:production.
Como o sub é montadoHow sub is built
| Quando o jobWhen the job | sub |
|---|---|
| referencia um environmentreferences an environment | repo:ORG/REPO:environment:NAME |
| roda de uma branch (sem environment)runs from a branch (no environment) | repo:ORG/REPO:ref:refs/heads/BRANCH |
| roda por pull request (sem environment)runs for a pull request (no environment) | repo:ORG/REPO:pull_request |
| roda de uma tag (sem environment)runs from a tag (no environment) | repo:ORG/REPO:ref:refs/tags/TAG |
sub usa a forma environment:NAME e não mais a da branch. Por isso a trust policy acima usa environment:develop, e não ref:refs/heads/develop.When a job references an environment, sub uses the environment:NAME form, not the branch form. That is why the trust policy above uses environment:develop rather than ref:refs/heads/develop.repo:<ORG>@<ORG_ID>/<REPO>@<REPO_ID>:environment:develop.Repositories created after July 15, 2026, or that opted in, use the immutable format with the owner and repository IDs. In that case a name-only trust policy fails with “Not authorized to perform sts:AssumeRoleWithWebIdentity”. The value becomes, for example: repo:<ORG>@<ORG_ID>/<REPO>@<REPO_ID>:environment:develop.Permissões da roleRole permissions
A role só precisa do mínimo para este deploy. A lista abaixo junta as permissões que as documentações das actions de ECR e ECS indicam; ajuste ARNs e, se usar CodeDeploy, acrescente o que a documentação da action pedir.The role only needs the minimum for this deploy. The list below combines the permissions that the ECR and ECS action docs call for; adjust the ARNs and, if you use CodeDeploy, add what the action docs ask for.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EcrLogin",
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
},
{
"Sid": "EcrPush",
"Effect": "Allow",
"Action": [
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
],
"Resource": "arn:aws:ecr:<REGION>:<ACCOUNT_ID>:repository/<ECR_REPOSITORY>"
},
{
"Sid": "EcsRegister",
"Effect": "Allow",
"Action": "ecs:RegisterTaskDefinition",
"Resource": "*"
},
{
"Sid": "EcsPassRoles",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": [
"arn:aws:iam::<ACCOUNT_ID>:role/<TASK_ROLE>",
"arn:aws:iam::<ACCOUNT_ID>:role/<TASK_EXECUTION_ROLE>"
]
},
{
"Sid": "EcsDeploy",
"Effect": "Allow",
"Action": [
"ecs:UpdateService",
"ecs:DescribeServices"
],
"Resource": "arn:aws:ecs:<REGION>:<ACCOUNT_ID>:service/<CLUSTER>/<SERVICE>"
}
]
}
3. Environments no GitHub3. GitHub Environments
No repositório: Settings → Environments → New environment. Crie develop e production.In the repository: Settings → Environments → New environment. Create develop and production.
- Deployment branches. Em
develop, permita só a branchdevelop; emproduction, só amain. É o que impede outra branch de obter acesso à role.Deployment branches. Ondevelop, allow only thedevelopbranch; onproduction, onlymain. This is what stops another branch from reaching the role. - Required reviewers em
production(até 6 pessoas ou times; basta uma aprovação), se quiser aprovação manual antes do deploy.Required reviewers onproduction(up to 6 people or teams; one approval is enough), if you want manual approval before deploying. - Variables por ambiente:
AWS_ROLE_ARN,AWS_REGION,ECR_REPOSITORY,ECS_CLUSTER,ECS_SERVICE. Ficam disponíveis só para jobs que usam o environment, pelo contextovars.Variables per environment:AWS_ROLE_ARN,AWS_REGION,ECR_REPOSITORY,ECS_CLUSTER,ECS_SERVICE. They are available only to jobs that use the environment, through thevarscontext.
A AWS recomenda proteger environments usados em políticas OIDC com regras de proteção, como a restrição de branches e tags de deploy.AWS recommends protecting environments used in OIDC policies with protection rules, such as deployment branch and tag restrictions.
4. O workflow4. The workflow
Um único arquivo atende os dois ambientes: o nome do environment sai do nome da branch. Arquivos no repositório:One file serves both environments: the environment name comes from the branch name. Files in the repository:
.github/workflows/deploy.yml
.aws/task-definition.json # aws ecs register-task-definition --generate-cli-skeleton
name: Deploy
on:
push:
branches:
- develop
- main
# One deploy at a time per branch; a newer push waits for the running one.
concurrency:
group: deploy-${{ github.ref_name }}
cancel-in-progress: false
permissions:
id-token: write # lets the job request the OIDC token
contents: read # actions/checkout
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ github.ref_name == 'main' && 'production' || 'develop' }}
env:
AWS_REGION: ${{ vars.AWS_REGION }}
ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
ECS_CLUSTER: ${{ vars.ECS_CLUSTER }}
ECS_SERVICE: ${{ vars.ECS_SERVICE }}
ECS_TASK_DEFINITION: .aws/task-definition.json
CONTAINER_NAME: app
steps:
- uses: actions/checkout@v6
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ vars.AWS_ROLE_ARN }}
role-session-name: gh-${{ github.run_id }}
aws-region: ${{ env.AWS_REGION }}
- name: Log in to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push the image
id: build-image
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t "$REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" .
docker push "$REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
echo "image=$REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> "$GITHUB_OUTPUT"
- name: Render the task definition
id: render
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_DEFINITION }}
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image.outputs.image }}
- name: Deploy to Amazon ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.render.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
permissions: id-token: writepermite pedir o token OIDC e não dá acesso de escrita a nenhum recurso;contents: readé para o checkout.permissions: id-token: writelets the job request the OIDC token and grants no write access to any resource;contents: readis for the checkout.concurrencyfaz dois pushes na mesma branch não implantarem ao mesmo tempo.concurrencykeeps two pushes to the same branch from deploying at the same time.- A imagem é marcada com o SHA do commit, então cada deploy é rastreável e reversível.The image is tagged with the commit SHA, so each deploy is traceable and reversible.
role-session-namecom o ID da execução faz o CloudTrail mostrar qual execução do workflow fez cada chamada.role-session-namewith the run ID makes CloudTrail show which workflow run made each call.wait-for-service-stability: truefaz o job esperar o serviço estabilizar; se o deploy falhar, o job falha.wait-for-service-stability: truemakes the job wait for the service to stabilise; if the deploy fails, the job fails.
@v6, @v2, @v1) por legibilidade. Em produção, fixe por SHA.These actions are not certified by GitHub. GitHub’s docs recommend pinning actions to a commit SHA; the examples use tags (@v6, @v2, @v1) for readability. In production, pin to a SHA.Cuidados e problemas comunsSafety and common problems
- “Not authorized to perform sts:AssumeRoleWithWebIdentity”. O
subda trust policy não bate com o do token: confira se o job referencia o environment, o nome exato dele e o formato imutável.“Not authorized to perform sts:AssumeRoleWithWebIdentity”. The trust policy’ssubdoes not match the token’s: check that the job references the environment, its exact name, and the immutable format. - Nunca use só curinga no
sub. O IAM rejeita uma trust policy do GitHub sem a condição, ou com valor que seja só*.Never use only a wildcard insub. IAM rejects a GitHub trust policy without the condition, or whose value is only*. - Evite
ForAllValues:em statementsAllow: pode liberar acesso quando a claim está ausente ou escrita errada. UseStringEqualsouStringLike.AvoidForAllValues:inAllowstatements: it can grant access when a claim is absent or misspelled. UseStringEqualsorStringLike. - Cuidado com gatilhos
pull_request_target. Jobs do Dependabot têmevent_nameigual adynamic; se a trust policy deve valer só para Actions, restrinja os eventos esperados.Be careful withpull_request_targettriggers. Dependabot jobs haveevent_nameset todynamic; if the trust policy should cover only Actions, restrict the expected events. - A AWS não suporta claims OIDC personalizadas; o GitHub permite personalizar o formato do
sub, mas crie antes a condição correspondente na AWS.AWS does not support custom OIDC claims; GitHub lets you customise thesubformat, but create the matching condition in AWS first. - A sessão dura 1 hora por padrão (
role-duration-seconds, de 15 min a 12 h). Deploys longos precisam de uma duração maior.The session lasts 1 hour by default (role-duration-seconds, from 15 min to 12 h). Long deploys need a longer duration.
- GitHub Docs · Configuring OpenID Connect in Amazon Web Services
- GitHub Docs · OpenID Connect reference (sub claim formats)
- GitHub Docs · Managing environments for deployment
- GitHub Docs · Workflow syntax (push branches, concurrency, permissions, environment)
- GitHub Docs · Deploying to Amazon Elastic Container Service
- AWS Docs · Create an OpenID Connect (OIDC) identity provider in IAM
- AWS Docs · Create a role for OpenID Connect federation (GitHub section)
- aws-actions/configure-aws-credentials
- aws-actions/amazon-ecr-login
- aws-actions/amazon-ecs-deploy-task-definition