Carrega secrets d'AWS SSM i Azure Key Vault a GitHub Actions
Com fas servir secrets d'AWS SSM Parameter Store o Azure Key Vault en un job de GitHub Actions sense copiar-los a GitHub Secrets? Autentica el job amb OIDC i deixa que l'Action d'Envilder llegeixi el teu envilder.json versionat i exporti els secrets als passos següents.
Com funciona l'Action
L'Action és un pas compost: instal·la Node.js 22, llegeix el fitxer de mapeig, obté cada secret amb les credencials de núvol que ja té el job i emmascara cada valor al log. Per defecte, després exporta els secrets a l'entorn de tots els passos posteriors del job; amb command o env-file, en canvi, executa una ordre o escriu un fitxer .env. No inicia sessió a AWS ni a Azure per si mateixa, així que ho ha de fer un pas anterior (aws-actions/configure-aws-credentials o azure/login). Utilitza l'etiqueta publicada macalbert/envilder/github-action@v0; no cal cap pas de compilació.
Prerequisits
- actions/checkout abans de l'Action, perquè el fitxer de mapeig existeixi al runner.
- El permís id-token: write, perquè el job pugui demanar un token OIDC, i contents: read.
- AWS: un rol IAM que GitHub pugui assumir, fet servir a través d'aws-actions/configure-aws-credentials.
- Azure: un registre d'aplicació amb una credencial federada, fet servir a través d'azure/login.
- Un envilder.json al teu repositori
La GitHub Action només suporta el mode pull (sense push).
Triar un mode
L'Action té tres modes, i els inputs que defineixes en trien un. El mode per defecte només necessita map-file:
| Mode | Com es tria | Qui pot llegir els secrets | Fitxers escrits |
|---|---|---|---|
| Entorn del job (per defecte) | Només map-file | Tots els passos posteriors del job | El fitxer GITHUB_ENV del runner, al seu directori temporal (cap .env) |
| Ordre | command | Només aquesta ordre i els processos que inicia | Cap |
| Fitxer .env | env-file | Qualsevol pas o eina que llegeixi el fitxer | El fitxer .env |
En el mode per defecte, l'Action afegeix els secrets al fitxer GITHUB_ENV del runner, i el runner els posa a l'entorn de cada pas que s'executa després dins del mateix job, incloses les actions de tercers, les actions de contenidor Docker i els passos que s'executen dins d'un container del job. Els passos els llegeixen com qualsevol altra variable: process.env.DB_PASSWORD, os.environ["DB_PASSWORD"] o $DB_PASSWORD. Els altres jobs no els reben, així que cada job que necessita secrets torna a executar l'Action. Un contenidor que inicies tu mateix amb docker run no els hereta: passa'ls explícitament, per exemple amb docker run -e DB_PASSWORD.
Fes servir env-file només quan una eina no pot llegir els secrets de cap altra manera que d'un fitxer, per exemple docker compose --env-file. Millor una ruta dins de runner.temp, perquè el fitxer quedi fora de l'espai de treball del repositori. env-file i command no es poden combinar.
- uses: macalbert/envilder/github-action@v0
with:
map-file: envilder.json
env-file: ${{ runner.temp }}/envilder.env
- run: docker compose --env-file "${{ runner.temp }}/envilder.env" up -dNo escriure cap fitxer .env elimina la via més habitual per on es filtren secrets a CI: un fitxer que acaba a upload-artifact, a una memòria cau, a un COPY . . d'un Dockerfile o a un git add -A. Les eines posteriors encara poden escriure les variables en fitxers, així que tracta els resultats de build i de tests com ho faries sense Envilder. Tampoc protegeix contra codi maliciós dins del job, que pot llegir les variables d'entorn tan fàcilment com un fitxer. Tots els modes emmascaren els valors al log. Els modes d'entorn del job i d'ordre fallen de manera segura: si falta algun secret o és buit, el pas falla i no s'exporta ni s'inicia res.
AWS SSM Parameter Store amb OIDC
1. Permet que GitHub assumeixi un rol IAM
Afegeix token.actions.githubusercontent.com com a proveïdor d'identitat OpenID Connect a IAM (audiència sts.amazonaws.com, un cop per compte) i crea un rol amb aquesta política de confiança. La condició sub el limita a un repositori i una branca; fes servir repo:my-org/my-repo:environment:production per limitar-lo a un entorn de GitHub.
{
"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:my-org/my-repo:ref:refs/heads/main"
}
}
}]
}A github.com, els repositoris creats, reanomenats o transferits a partir del 15 de juliol de 2026, i els que han activat els subjectes immutables, reben un sub que també inclou els ID del propietari i del repositori: repo:my-org@OWNER_ID/my-repo@REPO_ID:ref:refs/heads/main. La resta mantenen el format només amb noms que es mostra a dalt. Consulta la configuració OIDC del teu repositori per saber el prefix exacte i fes servir el mateix valor a la política de confiança d'AWS i a la credencial federada d'Azure.
2. Permisos mínims
L'Action crida ssm:GetParameter amb desxifratge, un cop per cada entrada del fitxer de mapeig. No llista, escriu ni esborra paràmetres, així que és l'únic permís que necessita el rol. Limita'l a la ruta de la teva aplicació:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "ssm:GetParameter",
"Resource": "arn:aws:ssm:eu-west-1:123456789012:parameter/myapp/prod/*"
}]
}Els paràmetres SecureString xifrats amb la clau per defecte aws/ssm no necessiten res més. Si fan servir una clau KMS gestionada pel client, permet també kms:Decrypt sobre aquesta clau. El compte que surt al log ve de sts:GetCallerIdentity, que no necessita cap permís.
3. Fitxer de mapeig i workflow
El fitxer de mapeig és el mateix que fas servir amb la CLI: cada variable d'entorn apunta al nom d'un paràmetre.
{
"DB_PASSWORD": "/myapp/prod/db-password",
"STRIPE_KEY": "/myapp/prod/stripe-key"
}name: Deploy Application
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::123456789012:role/github-envilder
aws-region: eu-west-1
- name: Pull Secrets from AWS SSM
uses: macalbert/envilder/github-action@v0
with:
map-file: envilder.json
- run: npm ci
- run: npm run deploySortida esperada
El log del pas mostra el mode, la identitat d'AWS que s'utilitza, cada variable resolta amb el valor emmascarat i un resum:
🔑 Envilder GitHub Action - Starting...
📋 Map file: envilder.json
🧠 Mode: job environment (secrets in every later step)
☁ AWS identity · account=123456789012 · region=eu-west-1 · profile=default
🪙 RESOLVING SECRETS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ DB_PASSWORD → *****************x9Q
✓ STRIPE_KEY → *****************************4Rt
envilder: 2 secrets exported to the job environment (DB_PASSWORD, STRIPE_KEY)
✅ Secrets loaded successfully!Cada valor es registra també amb l'ordre add-mask de GitHub, de manera que un pas posterior que l'imprimeixi mostra ***. Si fas servir env-file, el fitxer .env es queda al runner; no el pugis com a artefacte.
Azure Key Vault amb OIDC
1. Crea una credencial federada
Crea un registre d'aplicació i el seu service principal, i afegeix-hi una credencial federada que confiï en els tokens d'un repositori i una branca. No es crea cap secret de client. El subjecte fa servir el mateix format que el sub d'AWS, inclosa la variant immutable quan el teu repositori l'emet.
az ad app create --display-name github-envilder
az ad sp create --id {APP_ID}
az ad app federated-credential create --id {APP_ID} --parameters '{
"name": "github-main",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:my-org/my-repo:ref:refs/heads/main",
"audiences": ["api://AzureADTokenExchange"]
}'2. Rol mínim
Key Vault Secrets User permet llegir els valors dels secrets i res més. Assigna'l sobre el vault, no sobre el grup de recursos ni la subscripció:
az role assignment create \
--role "Key Vault Secrets User" \
--assignee {APP_ID} \
--scope /subscriptions/{SUB}/resourceGroups/{RG}/providers/Microsoft.KeyVault/vaults/{VAULT}Si el vault fa servir polítiques d'accés en lloc d'Azure RBAC, concedeix get sobre els secrets: az keyvault set-policy --name {VAULT} --spn {APP_ID} --secret-permissions get.
3. Fitxer de mapeig i workflow
Amb $config al fitxer de mapeig, el workflow no necessita els inputs provider ni vault-url. Els noms de Key Vault només admeten lletres, dígits i guions, així que Envilder converteix les rutes: /myapp/prod/stripe-key es llegeix com a myapp-prod-stripe-key (sense la barra inicial, barres convertides en guions).
{
"$config": {
"provider": "azure",
"vaultUrl": "https://my-vault.vault.azure.net"
},
"DB_PASSWORD": "myapp-prod-db-password",
"STRIPE_KEY": "/myapp/prod/stripe-key"
}name: Deploy with Azure Key Vault
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
- name: Pull Secrets from Azure Key Vault
uses: macalbert/envilder/github-action@v0
with:
map-file: envilder.json
- run: npm ci
- run: npm run deployEls ID de client, tenant i subscripció identifiquen l'aplicació; no són credencials, així que n'hi ha prou amb variables del repositori. Si s'informen, els inputs provider i vault-url sobreescriuen el $config del fitxer de mapeig.
Workflow multi-entorn
Tingues un fitxer de mapeig per entorn i tria'l des de l'input del workflow. Un job que defineix environment rep un token OIDC amb un sub acabat en :environment:<nom> en lloc de :ref:refs/heads/main, així que la política de confiança d'AWS (o la credencial federada d'Azure) de cada entorn ha de fer servir aquest subjecte: repo:my-org/my-repo:environment:production, o repo:my-org@OWNER_ID/my-repo@REPO_ID:environment:production si el teu repositori emet subjectes immutables. Després, cada entorn de GitHub pot tenir el seu ARN de rol i les seves regles de protecció.
name: Deploy to Environment
on:
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
type: choice
options: [dev, staging, production]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v5
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ vars.AWS_ROLE_ARN }}
aws-region: eu-west-1
- name: Pull ${{ inputs.environment }} secrets
uses: macalbert/envilder/github-action@v0
with:
map-file: config/${{ inputs.environment }}/envilder.json
- run: npm ci
- run: npm run deployExecutar una ordre amb els secrets
Amb l'input command, l'Action executa aquesta ordre amb bash, des de l'arrel del repositori, amb els secrets al seu entorn, i acaba el pas amb el codi de sortida de l'ordre. Els secrets no s'exporten, així que els passos posteriors, incloses les actions de tercers, no els veuen mai. Fes-lo servir quan només un pas necessita els secrets i vols la mínima exposició.
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::123456789012:role/github-envilder
aws-region: eu-west-1
- name: Deploy with secrets
uses: macalbert/envilder/github-action@v0
with:
map-file: envilder.json
command: ./deploy.sh --prod
- run: echo "$DB_PASSWORD" # empty: the secrets stayed in the deploy stepTambé pots executar la CLI tu mateix amb npx envilder exec --map=envilder.json -- <ordre> en un pas run. Necessita Node.js 22.12 o posterior (actions/setup-node), i el seu stderr s'ha de quedar al log del pas, perquè és d'allà d'on GitHub llegeix les màscares.
Per què no copiar els secrets a GitHub Secrets
Copiar els valors als secrets del repositori funciona fins que un valor canvia. A partir d'aquí n'hi ha dues còpies:
- Rotació: quan es rota un secret a SSM o Key Vault, cada repositori amb una còpia continua desplegant el valor antic fins que algú l'actualitza a mà.
- Divergència i auditoria: GitHub Secrets no es pot tornar a llegir, així que no pots comprovar si la còpia encara coincideix amb l'origen. Llegir del núvol queda registrat amb el rol o l'aplicació que ha fet cada petició: a CloudTrail a AWS, i als logs d'auditoria de Key Vault a Azure un cop activada la configuració de diagnòstic del vault.
- Claus de llarga durada: copiar secrets sol anar acompanyat d'una clau d'accés d'AWS o un secret de client d'Azure guardats també a GitHub. Amb OIDC el job rep un token de curta durada, i res del que hi ha guardat a GitHub funciona fora del workflow.
- Una sola llista: el fitxer de mapeig es versiona amb el codi, es revisa a les pull requests i és el mateix fitxer que fan servir els desenvolupadors en local amb la CLI.
GitHub Secrets continua sent una bona opció per a valors que només existeixen per al pipeline, com un token d'un servei de desplegament extern.
Errors habituals
Missatges tal com surten al log del pas i com resoldre'ls. Envilder emmascara la ruta del secret que mostra al costat de cada variable, excepte els tres últims caràcters. Els missatges que vénen d'AWS o d'Azure es mostren tal com arriben i poden incloure el nom complet del paràmetre o del recurs.
No es pot llegir el fitxer de mapeig
🚨 Failed to initialize: Failed to read map file: envilder.jsonFalta el pas de checkout o s'executa després de l'Action, o map-file apunta a un lloc incorrecte. Les rutes són relatives a l'arrel del repositori.
No es pot assumir el rol
Could not assume role with OIDC: Not authorized to perform sts:AssumeRoleWithWebIdentityAquest error ve d'aws-actions/configure-aws-credentials, abans que s'executi Envilder. Comprova que el workflow té id-token: write i que el sub de la política de confiança coincideix exactament amb el repositori, la branca o l'entorn, en el format de subjecte que fa servir el teu repositori (amb ID immutables o sense). Els jobs de tag, de pull_request i amb environment generen un sub diferent.
Sense credencials d'AWS
💥 GAME OVER — some secrets could not be fetched
✗ DB_PASSWORD → *********************ord
⭐ WHY?
Could not load credentials from any providersCap pas ha autenticat el job a AWS abans de l'Action. Afegeix aws-actions/configure-aws-credentials abans, al mateix job: les credencials no passen d'un job a un altre.
Accés denegat a un paràmetre
User: arn:aws:sts::123456789012:assumed-role/github-envilder/GitHubActions is not authorized to perform: ssm:GetParameter on resource: arn:aws:ssm:eu-west-1:123456789012:parameter/myapp/prod/db-password because no identity-based policy allows the ssm:GetParameter actionEl rol és vàlid però la seva política no cobreix aquest paràmetre. Revisa la ruta de Resource i la regió de l'ARN, que ha de coincidir amb l'aws-region del pas de credencials.
Credencials caducades
AWS credentials are expired or invalid. Your security token or SSO session may have expired. Refresh your credentials and retry (for SSO, run: aws sso login).La sessió del rol ha acabat abans que s'executés el pas (una hora per defecte). Executa Envilder just després del pas de credencials, o augmenta role-duration-seconds juntament amb la durada màxima de sessió del rol.
Paràmetre o secret inexistent
✗ STRIPE_KEY secret not found (path: *******************key) — skippedRevisa el nom, la regió i, a Azure, el nom convertit. En el mode per defecte i amb command, un secret que falta o és buit fa fallar el pas: no s'exporta res i la comanda no s'inicia. Amb env-file és un avís, no un error: el pas acaba bé, amb menys secrets al resum que al mapeig. En un fitxer .env nou la variable queda fora. Si el fitxer ja existia (al repositori, a la memòria cau o escrit per un pas anterior), es conserva el valor antic, així que esborra el fitxer abans de l'Action quan un valor obsolet no hagi de sobreviure. Si un valor que falta ha d'aturar el desplegament, fes servir el mode per defecte o command en lloc d'env-file.
Falta l'URL del vault d'Azure
vaultUrl is required when using Azure provider. Set it in $config.vaultUrl in your map file or via --vault-url flag.Informa l'input vault-url o $config.vaultUrl al fitxer de mapeig. Fes servir l'URI del vault que mostra el portal d'Azure, per exemple https://my-vault.vault.azure.net.
Accés denegat a Azure Key Vault
Caller is not authorized to perform action on resource.azure/login ha funcionat però la identitat no té cap rol de pla de dades sobre el vault. Assigna Key Vault Secrets User sobre el vault i espera uns minuts, perquè les assignacions de rol triguen a propagar-se. Els vaults amb polítiques d'accés diuen en canvi que la identitat no té el permís get de secrets.
Perfil d'AWS al fitxer de mapeig
Si envilder.json defineix $config.profile per a ús local, l'Action passa aquest perfil a l'SDK d'AWS. Els runners de GitHub no tenen perfils d'AWS, i amb un perfil definit l'SDK ignora les credencials de configure-aws-credentials. Treu el perfil del mapeig que fas servir a CI, o tingues un mapeig separat per a CI.
Inputs i outputs de l'Action
Inputs
| Input | Requerit | Per defecte | Descripció |
|---|---|---|---|
map-file | Sí | - | Ruta al fitxer JSON de mapeig |
env-file | No | - | Opcional. Ruta a un fitxer .env per escriure, per a eines que necessiten un fitxer. Sense env-file ni command, els secrets van a l'entorn del job |
command | No | - | Opcional. Ordre que s'executa (amb bash) amb els secrets només al seu entorn. No es pot combinar amb env-file |
provider | No | aws | aws o azure |
vault-url | No | - | URL d'Azure Key Vault |
Outputs
| Output | Descripció |
|---|---|
env-file-path | Ruta al fitxer .env generat (buida si no es defineix env-file) |