• Una cuenta de AWS.
  • Una identidad administrativa con permisos para administrar proveedores OIDC, roles y políticas de IAM.
  • AWS CLI v2 autenticada en la cuenta correcta.
  • Un repositorio de GitHub.com con permiso para editar .github/workflows.
  • El propietario, nombre, rama, tag o Environment autorizado del repositorio.
  • jq para validar los documentos JSON.
  • Una región AWS, por ejemplo us-east-1.

Comprueba primero el entorno local:

aws --version
jq --version
aws sts get-caller-identity --output json

Verifica que Account corresponda a la cuenta que deseas modificar.

El número de cuenta y el ARN del rol no son credenciales, pero evita publicar identificadores internos si la política de tu organización los considera sensibles.

Paso 1: definir qué workflow será confiable

AWS debe validar un valor sub exacto. El contexto del job cambia su formato: una rama, un tag, un pull request y un GitHub Environment no producen el mismo sujeto.

Si el job declara environment, el Environment sustituye a la rama o al tag dentro de sub.

EscenarioFormato anteriorFormato inmutable desde 15/07/2026
Rama mainrepo:ORG/REPO:ref:refs/heads/mainrepo:ORG@ORG_ID/REPO@REPO_ID:ref:refs/heads/main
Tag v1.2.0repo:ORG/REPO:ref:refs/tags/v1.2.0repo:ORG@ORG_ID/REPO@REPO_ID:ref:refs/tags/v1.2.0
Environment productionrepo:ORG/REPO:environment:productionrepo:ORG@ORG_ID/REPO@REPO_ID:environment:production
Pull requestrepo:ORG/REPO:pull_requestrepo:ORG@ORG_ID/REPO@REPO_ID:pull_request

Para conocer el formato de tu repositorio, revisa la vista previa de OIDC en la configuración del repositorio u organización.

También puedes utilizar temporalmente el depurador citado por la documentación de GitHub. No imprimas el JWT completo: solo necesitas comprobar el valor exacto de sub.

Para producción, conviene usar un GitHub Environment con revisores y reglas de despliegue. En ese caso, la política debe confiar en environment:production, no en ref:refs/heads/main.

Paso 2: crear o comprobar el proveedor OIDC

El proveedor se crea una sola vez por cuenta de AWS.

  • Issuer: https://token.actions.githubusercontent.com
  • Audiencia habitual: sts.amazonaws.com

AWS no necesita un thumbprint manual de GitHub mientras pueda validar la cadena TLS mediante su biblioteca de autoridades raíz confiables. Recurre al thumbprint cuando esa validación no es posible.

Define el ID de la cuenta y el ARN del proveedor:

AWS_ACCOUNT_ID=123456789012

OIDC_PROVIDER_ARN="arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com"

Comprueba si el proveedor ya existe:

aws iam get-open-id-connect-provider \
  --open-id-connect-provider-arn "$OIDC_PROVIDER_ARN"

Si devuelve Url y ClientIDList con sts.amazonaws.com, reutilízalo.

Si devuelve NoSuchEntity, créalo:

aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com

Comprueba la configuración:

aws iam get-open-id-connect-provider \
  --open-id-connect-provider-arn "$OIDC_PROVIDER_ARN" \
  --query '{Url:Url,Audiences:ClientIDList}'

No intentes crear otro proveedor con la misma URL si ya existe. No mejora el aislamiento y AWS rechazará la duplicación.

Paso 3: crear la política de confianza

La política de confianza responde quién puede asumir el rol.

El ejemplo siguiente autoriza únicamente la rama main del repositorio indicado y utiliza StringEquals para evitar comodines.

Sustituye el sujeto por el valor exacto emitido por tu repositorio.

Crea trust-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012: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:mi-organizacion@123456/mi-repositorio@789012:ref:refs/heads/main"
        }
      }
    }
  ]
}

Valida primero la sintaxis:

jq empty trust-policy.json

Si tienes permiso para utilizar IAM Access Analyzer, revisa también sus hallazgos:

aws accessanalyzer validate-policy \
  --policy-type RESOURCE_POLICY \
  --policy-document file://trust-policy.json

Esta validación detecta errores y recomendaciones, pero no confirma que sub coincida con el JWT real. Esa coincidencia se comprueba ejecutando el workflow.

Crea el rol:

ROLE_NAME=github-actions-deploy

aws iam create-role \
  --role-name "$ROLE_NAME" \
  --assume-role-policy-document file://trust-policy.json \
  --max-session-duration 3600

Si el rol ya existe, conserva una copia de la confianza actual antes de sustituirla:

aws iam get-role \
  --role-name "$ROLE_NAME" \
  --query 'Role.AssumeRolePolicyDocument' \
  --output json > trust-policy.backup.json

Aplica la política actualizada:

aws iam update-assume-role-policy \
  --role-name "$ROLE_NAME" \
  --policy-document file://trust-policy.json
Evita la confianza amplia. No utilices repo:*/*:*, repo:ORG/*:* ni un asterisco global en sub para “hacer que funcione”. Autorizaría más repositorios, referencias o contextos de los previstos. Usa StringLike solo cuando el comodín sea una decisión explícita y acotada.

Paso 4: asignar permisos mínimos al rol

La confianza permite obtener una sesión. La política de permisos determina qué puede hacer esa sesión.

No adjuntes AdministratorAccess para probar OIDC. aws sts get-caller-identity permite verificar la sesión sin conceder acceso a un servicio de negocio.

Cuando agregues una operación real, limita acciones y recursos.

El siguiente ejemplo permite leer objetos de un bucket ficticio. No lo utilices si tu workflow no necesita S3.

Crea permissions-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::ejemplo-artifacts/*"
    }
  ]
}

Aplica la política solo si esa operación es necesaria:

aws iam put-role-policy \
  --role-name "$ROLE_NAME" \
  --policy-name ArtifactReadOnly \
  --policy-document file://permissions-policy.json

Paso 5: crear el workflow

El permiso id-token: write permite solicitar un JWT. No concede por sí mismo escritura en AWS.

El ejemplo fija aws-actions/configure-aws-credentials al SHA completo del release oficial e inmutable v6.2.3, publicado el 22 de julio de 2026.

En runners autohospedados, la serie v6 requiere GitHub Actions Runner 2.327.1 o posterior.

Crea .github/workflows/verify-aws-oidc.yml:

name: Verificar AWS OIDC

on:
  workflow_dispatch:

permissions:
  contents: read
  id-token: write

jobs:
  verify:
    runs-on: ubuntu-latest

    steps:
      - name: Configurar credenciales temporales de AWS
        uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
          role-session-name: github-${{ github.run_id }}
          aws-region: us-east-1
          allowed-account-ids: 123456789012

      - name: Comprobar la identidad asumida
        run: aws sts get-caller-identity --output json

Fijar una action a un SHA evita que una etiqueta mutable cambie el código ejecutado.

Configura Dependabot para recibir actualizaciones y compara cualquier nuevo SHA con el release oficial antes de sustituirlo.

Eventos no confiables. No concedas un rol de despliegue a código controlado por colaboradores externos. Revisa especialmente pull_request_target, workflows reutilizables, runners persistentes y cualquier paso que ejecute contenido del repositorio antes de usar las credenciales.

Paso 6: ejecutar y comprobar

Ejecuta el workflow manualmente desde la referencia autorizada. Si la política confía en main, selecciona main.

En el step “Comprobar la identidad asumida”, la salida esperada tendrá esta forma:

{
  "UserId": "AROAXXXXXXXXXXXXX:github-123456789",
  "Account": "123456789012",
  "Arn": "arn:aws:sts::123456789012:assumed-role/github-actions-deploy/github-123456789"
}

Comprueba que:

  • Account sea la cuenta esperada.
  • Arn contenga assumed-role/github-actions-deploy/.
  • El nombre de sesión corresponda a github-${{ github.run_id }}.

Ejecutar una prueba negativa

  1. Crea una rama que contenga el mismo archivo de workflow.
  2. Ejecuta workflow_dispatch seleccionando esa rama.
  3. Si la confianza solo acepta main, AWS STS debe rechazar la solicitud.

El error esperado será similar a:

Not authorized to perform sts:AssumeRoleWithWebIdentity

Para probar un Environment, declara el Environment en el job:

jobs:
  verify:
    environment: production
    runs-on: ubuntu-latest

Configura antes los revisores y reglas de protección correspondientes.

Criterio de aceptación. La prueba positiva obtiene una sesión del rol esperado y la prueba negativa no puede asumirlo. Que GetCallerIdentity funcione confirma la autenticación, pero no demuestra que el rol tenga permiso para S3, ECS, Route 53 u otro servicio.

Migrar desde claves estáticas

  • Mantén las claves existentes únicamente durante la validación controlada.
  • No las mezcles como fallback automático dentro del mismo job.
  • Valida OIDC con la identidad, la operación real y la prueba negativa.
  • Elimina AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY y AWS_SESSION_TOKEN de los secrets y variables correspondientes.
  • Desactiva o elimina la access key anterior en IAM.
  • Revisa CloudTrail para confirmar que la clave dejó de utilizarse.
  • Documenta el rol, el sujeto autorizado, el propietario de la política y el procedimiento de revocación.

Problemas frecuentes

Token OIDC

Comprueba que permissions incluya:

permissions:
  id-token: write

La declaración puede estar en el workflow o en el job.

Si definiste permissions en varios niveles, recuerda que una declaración más específica puede sustituir los permisos heredados.

Not authorized to perform sts:AssumeRoleWithWebIdentity

Compara carácter por carácter aud y sub con la política de confianza.

Los fallos habituales son:

  • Una rama distinta.
  • Un Environment presente en el job.
  • Diferencias de mayúsculas o nombres.
  • El ARN de un proveedor perteneciente a otra cuenta.
  • El nuevo formato inmutable con @ORG_ID y @REPO_ID.
  • Una audiencia diferente de sts.amazonaws.com.

NoSuchEntity o proveedor inexistente

Confirma que el ARN:

  • Use el ID de la misma cuenta del rol.
  • Termine en oidc-provider/token.actions.githubusercontent.com.
  • Corresponda a un proveedor cuya ClientIDList contenga sts.amazonaws.com.

La sesión se crea, pero el servicio devuelve AccessDenied

En este caso, OIDC ya funcionó.

Revisa:

  • La política de permisos del rol.
  • El ARN del recurso.
  • Las condiciones de la política.
  • Los permission boundaries.
  • Las políticas de AWS Organizations.
  • Las políticas del recurso.
  • Los eventos de CloudTrail.

Funciona desde main, pero falla con un Environment

Cuando el job declara environment, el sujeto pasa a:

repo:ORG/REPO:environment:NOMBRE

o, con el formato inmutable:

repo:ORG@ORG_ID/REPO@REPO_ID:environment:NOMBRE

Actualiza la política de confianza y configura las reglas de protección del Environment.

Buenas prácticas para producción

  • Crea roles separados por entorno y responsabilidad.
  • No compartas un rol de producción con desarrollo.
  • Usa GitHub Environments con revisores y reglas de despliegue.
  • Fija las actions a un SHA completo.
  • Automatiza propuestas de actualización con Dependabot.
  • Mantén la duración de sesión tan corta como permita el job.
  • Evita exportar credenciales como outputs salvo que exista una necesidad justificada.
  • Revisa AssumeRoleWithWebIdentity y las acciones posteriores en CloudTrail.
  • Elimina las trust policies de repositorios archivados, transferidos o renombrados.
  • Adopta sujetos inmutables donde corresponda.
  • No imprimas el JWT ni las variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY o AWS_SESSION_TOKEN.

Conclusión

Una integración OIDC segura está completa cuando:

  • El proveedor de GitHub existe.
  • La confianza del rol acepta únicamente el sujeto previsto.
  • El workflow solicita el token mediante id-token: write.
  • La prueba positiva obtiene el rol correcto.
  • Una referencia no autorizada no puede asumirlo.
  • La política del rol concede únicamente los permisos necesarios.

El siguiente paso es sustituir el ejemplo de S3 por la política mínima de tu carga, probar la operación en un entorno no productivo y eliminar las claves estáticas después de confirmar que ya no se utilizan.

Cloud Serversby Donweb

Todo el poder de la nube a tus proyectos y aplicaciones.
Alojamiento ultra rápido, escalable y con alta disponibilidad.

  • Performance que te sorprenderá
  • Rápida escalabilidad y sin limitaciones
  • Arquitectura de alta disponibilidad
  • Soporte experto y ejecutivos de cuenta
  • Pagos en tu moneda y facturación local

Descubre la mejor solución de Cloud Hosting