Carrega paràmetres d'AWS SSM Parameter Store en un fitxer .env

Com portes els paràmetres d'AWS SSM Parameter Store a un fitxer .env local sense escriure cap script a mà? Llista una sola vegada cada variable i la ruta del seu paràmetre a envilder.json i executa envilder: llegeix cada paràmetre, desxifra els valors SecureString i escriu un .env que, un cop llegit de nou, dona els mateixos valors.

Requisits previs

  • Node.js 22.12 o superior. Executa la CLI amb npx envilder o instal·la-la amb npm install -g envilder.
  • Credencials d'AWS que l'SDK pugui trobar: un perfil amb nom, variables d'entorn o una sessió SSO iniciada amb aws sso login --profile <nom>.
  • Accés de lectura a cada paràmetre que mapis. La CLI crida GetParameter una vegada per variable, amb el desxifrat activat.

La política IAM mínima permet ssm:GetParameter sobre els ARN dels paràmetres del teu fitxer de mapa. Un comodí sobre el prefix de la ruta cobreix una aplicació i un entorn:

policy.jsonjson
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ssm:GetParameter",
      "Resource": "arn:aws:ssm:eu-west-1:123456789012:parameter/my-app/dev/*"
    },
    {
      "Effect": "Allow",
      "Action": "kms:Decrypt",
      "Resource": "arn:aws:kms:eu-west-1:123456789012:key/<key-id>"
    }
  ]
}

La segona sentència només cal per als paràmetres xifrats amb una clau KMS gestionada pel client; substitueix <key-id> per l'ID d'aquesta clau.

Les ordres push de l'exemple de sota necessiten un permís més, ssm:PutParameter, sobre els mateixos ARN. El push fa servir la clau KMS predeterminada del compte, així que no cal cap sentència kms:Encrypt. Concedeix-lo només a qui crea paràmetres: per fer pull n'hi ha prou amb la política de lectura de dalt.

Quina regió s'utilitza

Envilder tria la regió en aquest ordre i la mostra a la primera línia de cada execució (region=eu-west-1 a la sortida de sota):

  1. La variable d'entorn AWS_REGION.
  2. La variable d'entorn AWS_DEFAULT_REGION.
  3. La regió que l'SDK d'AWS troba per al perfil seleccionat (--profile, després $config.profile i després el predeterminat), per exemple region a ~/.aws/config.
  4. us-east-1, si no hi ha cap de les anteriors.

Els paràmetres guardats en una altra regió es publiquen com a no trobats, així que mira primer aquesta línia quan s'ometi un paràmetre que saps que existeix.

Exemple: de paràmetres SSM a un fitxer .env

Partim de zero i creem tres paràmetres sota /my-app/dev/, cadascun llegit en una variable:

Paràmetre SSMVariable al .env
/my-app/dev/db-passwordDB_PASSWORD
/my-app/dev/api-tokenAPI_TOKEN
/my-app/dev/database-urlDATABASE_URL
⚠️
El push sempre escriu el valor (PutParameter amb sobreescriptura), així que un paràmetre existent en rep una versió nova. --value es queda a l'historial del shell: fes-lo servir per a valors de prova i fes push dels secrets reals des d'un .env amb envilder --push.

Si els paràmetres ja existeixen, no els tornis a pujar: escriu envilder.json a mà amb la mateixa forma, un nom de variable per cada nom de paràmetre. Si una variable ja apunta a una altra ruta, el push surt amb 1 i no puja res; --no-map-update ometeix el fitxer de mapa. Referència del mode push: totes les opcions i les regles del fitxer de mapa

Fes push de cada valor una sola vegada. Un push d'un sol secret desa el valor com a paràmetre SecureString i, en el mateix pas, crea envilder.json o hi afegeix l'entrada (CLI 0.16 o superior). La primera ordre crea el fitxer:

envilder --push --key=DB_PASSWORD --value=local-dev-password \
  --secret-path=/my-app/dev/db-password
envilder --push --key=API_TOKEN --value=dev-token-7f3a9c21 \
  --secret-path=/my-app/dev/api-token
envilder --push --key=DATABASE_URL \
  --value='postgres://app:p@ss w0rd#1@db.internal:5432/app' \
  --secret-path=/my-app/dev/database-url

Línies de resultat de cada ordre (s'ometen la capçalera i la línia d'identitat). Els valors i les rutes estan emmascarats:

⭐ SECRET PUSHED  —  DB_PASSWORD=***************ord → ********************ord
✓ Created envilder.json

⭐ SECRET PUSHED  —  API_TOKEN=***************c21 → ******************ken
✓ Added API_TOKEN to envilder.json

⭐ SECRET PUSHED  —  DATABASE_URL=********************************************app → *********************url
✓ Added DATABASE_URL to envilder.json

envilder.json ja mapeja les tres variables. Només conté noms i rutes, mai valors:

envilder.jsonjson
{
  "DB_PASSWORD": "/my-app/dev/db-password",
  "API_TOKEN": "/my-app/dev/api-token",
  "DATABASE_URL": "/my-app/dev/database-url"
}

Executa envilder sense opcions. Pull és el mode per defecte: llegeix envilder.json i escriu .env al directori actual.

envilder
☁ AWS identity · account=123456789012 · region=eu-west-1 · profile=default

🪙  RESOLVING SECRETS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  ✓ DB_PASSWORD         → ***************ord
  ✓ API_TOKEN           → ***************c21
  ✓ DATABASE_URL        → ********************************************app

⭐ LEVEL CLEARED  —  3/3 secrets loaded · .env written

Els valors s'emmascaren: d'un valor de més de 10 caràcters només es mostren els tres últims, i les rutes s'emmascaren igual.

El .env resultant. Envilder cita DATABASE_URL perquè conté un espai i un #, i rellegeix el resultat com ho fa dotenv abans d'escriure:

.envdotenv
DB_PASSWORD=local-dev-password
API_TOKEN=dev-token-7f3a9c21
DATABASE_URL='postgres://app:p@ss w0rd#1@db.internal:5432/app'

Si l'executes de nou no canvia res: una línia que ja té el valor correcte no es reescriu, i les línies que el fitxer de mapa no declara es conserven.

⚠️
El fitxer .env conté els valors reals. Afegeix .env i .env.prod (que fem servir més avall) a .gitignore: el patró .env no coincideix amb .env.prod. envilder.json només conté noms i rutes, així que es pot commitejar sense problema.

Diversos entorns

Tingues un fitxer de mapa per entorn i tria'l amb --map. Els noms dels fitxers són teus. Aquest envilder.prod.json mapeja les mateixes variables als paràmetres de /my-app/prod/:

envilder.prod.jsonjson
{
  "DB_PASSWORD": "/my-app/prod/db-password",
  "API_TOKEN": "/my-app/prod/api-token",
  "DATABASE_URL": "/my-app/prod/database-url"
}

Passa --envfile pel fitxer que s'ha d'escriure i --profile pel perfil d'AWS que pot llegir aquests paràmetres:

envilder --map=envilder.prod.json --envfile=.env.prod --profile=prod-account
☁ AWS identity · account=123456789012 · region=eu-west-1 · profile=prod-account

🪙  RESOLVING SECRETS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  ✓ DB_PASSWORD         → *******************4aa
  ✓ API_TOKEN           → ****************0e3
  ✓ DATABASE_URL        → ***********************************app

⭐ LEVEL CLEARED  —  3/3 secrets loaded · .env.prod written

--profile substitueix $config.profile del fitxer de mapa, i AWS_REGION continua guanyant a la regió del perfil.

O prescindeix del fitxer amb envilder exec

Si només necessites els valors per arrencar un procés, envilder exec resol el mateix fitxer de mapa i arrenca la teva ordre amb els paràmetres afegits al seu entorn, sense escriure res al disc:

envilder exec -- node app.js
envilder: 3 secrets resolved (aws) → node …

El resum va a stderr, així que no es barreja amb la sortida de la teva ordre. L'ordre llegeix els valors com a variables d'entorn normals.

Quin triar

  • Fes servir envilder exec quan arrenques tu el procés: scripts d'npm, tests, migracions. No queda cap fitxer amb secrets al disc, i un paràmetre inexistent o il·legible atura l'ordre abans d'arrencar (codi de sortida 125).
  • Fes servir un fitxer .env quan una eina llegeix el fitxer per ruta o no pots embolcallar-ne l'ordre d'arrencada: env_file de Docker Compose, una configuració d'execució de l'IDE, un framework que carrega .env per si sol. En aquest mode un paràmetre inexistent només és un avís, així que mira el resum (2/3 en lloc de 3/3).

Comparat amb un script

L'alternativa habitual és un script de shell al voltant de get-parameters-by-path. Per a una exportació ràpida funciona:

aws ssm get-parameters-by-path --path /my-app/dev/ --recursive \
  --with-decryption --query 'Parameters[].[Name,Value]' --output text \
  | while IFS=$'\t' read -r name value; do
      echo "$(basename "$name" | tr 'a-z-' 'A-Z_')=$value"
    done > .env

On es queda curt quan el fitxer el comparteix un equip:

  • És fàcil oblidar --with-decryption. Sense aquesta opció un SecureString torna xifrat i l'script n'escriu el text xifrat.
  • L'API retorna com a màxim 10 paràmetres per pàgina. La AWS CLI segueix les pàgines per tu, però un script basat en un SDK o en una crida HTTP directa ha de seguir NextToken pel seu compte.
  • Els valors s'escriuen sense cometes: dotenv rellegeix DATABASE_URL=postgres://app:p@ss w0rd#1@db.internal:5432/app com postgres://app:p@ss w0rd, perquè el # comença un comentari.
  • > .env substitueix tot el fitxer, així que una línia com LOG_LEVEL=debug es perd, i els noms de les variables surten de l'últim segment de la ruta, de manera que reanomenar un paràmetre reanomena la variable sense avisar.
  • Res a Git diu quins paràmetres necessita l'aplicació. Amb envilder.json el contracte es revisa en una pull request, i una variable sense paràmetre surt com a avís.

L'script no necessita Node.js, fa una crida per pàgina de resultats en lloc d'una per variable, i exporta tot el que hi ha sota una ruta sense mapa. Envilder només llegeix els noms del fitxer de mapa, amb una crida GetParameter cadascun, així que necessita ssm:GetParameter, no ssm:GetParametersByPath.

Errors habituals

MissatgeSolució
Could not load credentials from any providersL'SDK d'AWS no ha trobat cap credencial. Executa aws sso login --profile <nom>, exporta credencials o passa --profile per a un perfil amb nom. Codi de sortida 1, cap .env escrit.
User: arn:aws:sts::123456789012:assumed-role/dev/session is not authorized to perform: ssm:GetParameter on resource: arn:aws:ssm:eu-west-1:123456789012:parameter/my-app/dev/api-token …Les credencials funcionen però no poden llegir aquest paràmetre. Permet ssm:GetParameter sobre el seu ARN, i kms:Decrypt per a una clau gestionada pel client. El fitxer .env no es toca.
secret not found (path: *******************dsn) — skippedEl paràmetre no existeix a la regió de la primera línia de la sortida. Comprova la ruta i la regió. El codi de sortida continua sent 0, així que mira l'1/2 del resum.
Invalid environment variable name "MY VAR": names may only contain letters, digits, underscore, dot, hyphen and slash, so that they can be read back from a .env fileReanomena la clau a envilder.json. L'execució s'atura abans de llegir cap paràmetre.
No map file found. Provide --map or create envilder.json in the current directory.Executa envilder des del directori que conté envilder.json, o passa --map=ruta/al/fitxer.json.

Tots els missatges del mode pull són a la referència del mode pull.

A GitHub Actions

A la CI, inicia la sessió del job a AWS amb OIDC i fes servir l'Action d'Envilder. Configura Node.js ella mateixa i, amb env-file, escriu el fitxer. Sense env-file exporta els valors als passos següents del job.

.github/workflows/ci.ymlyaml
name: CI
on: [push]

permissions:
  id-token: write # Required for OIDC
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
          aws-region: eu-west-1
      - uses: macalbert/envilder/github-action@v0
        with:
          map-file: envilder.json
          env-file: ${{ runner.temp }}/app.env
      - run: docker compose --env-file "$RUNNER_TEMP/app.env" up -d

El fitxer va al directori temporal del runner, fora de l'espai de treball, perquè els passos posteriors de build, cache o artifacts no el recullin. Per evitar el fitxer, fes servir l'input command de l'Action o envilder exec.