Carga parámetros de AWS SSM Parameter Store en un archivo .env
¿Cómo llevas los parámetros de AWS SSM Parameter Store a un archivo .env local sin escribir ningún script a mano? Lista una sola vez cada variable y la ruta de su parámetro en envilder.json y ejecuta envilder: lee cada parámetro, descifra los valores SecureString y escribe un .env que, al leerlo de nuevo, da los mismos valores.
Requisitos previos
- Node.js 22.12 o superior. Ejecuta la CLI con npx envilder o instálala con npm install -g envilder.
- Credenciales de AWS que el SDK pueda encontrar: un perfil con nombre, variables de entorno o una sesión SSO iniciada con aws sso login --profile <nombre>.
- Acceso de lectura a cada parámetro que mapees. La CLI llama a GetParameter una vez por variable, con el descifrado activado.
La política IAM mínima permite ssm:GetParameter sobre los ARN de los parámetros de tu archivo de mapa. Un comodín sobre el prefijo de la ruta cubre una aplicación y un entorno:
{
"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 segunda sentencia solo hace falta para los parámetros cifrados con una clave KMS administrada por el cliente; sustituye <key-id> por el ID de esa clave.
Los comandos push del ejemplo de abajo necesitan un permiso más, ssm:PutParameter, sobre los mismos ARN. El push usa la clave KMS predeterminada de la cuenta, así que no hace falta ninguna sentencia kms:Encrypt. Concédelo solo a quien crea parámetros: para hacer pull basta con la política de lectura de arriba.
Qué región se utiliza
Envilder elige la región en este orden y la muestra en la primera línea de cada ejecución (region=eu-west-1 en la salida de abajo):
- La variable de entorno AWS_REGION.
- La variable de entorno AWS_DEFAULT_REGION.
- La región que el SDK de AWS encuentra para el perfil seleccionado (--profile, luego $config.profile y luego el predeterminado), por ejemplo region en ~/.aws/config.
- us-east-1, si no hay ninguna de las anteriores.
Los parámetros guardados en otra región se notifican como no encontrados, así que mira primero esta línea cuando se omita un parámetro que sabes que existe.
Ejemplo: de parámetros SSM a un archivo .env
Partimos de cero y creamos tres parámetros bajo /my-app/dev/, cada uno leído en una variable:
| Parámetro SSM | Variable en .env |
|---|---|
/my-app/dev/db-password | DB_PASSWORD |
/my-app/dev/api-token | API_TOKEN |
/my-app/dev/database-url | DATABASE_URL |
Si los parámetros ya existen, no los vuelvas a subir: escribe envilder.json a mano con la misma forma, un nombre de variable por cada nombre de parámetro. Si una variable ya apunta a otra ruta, el push sale con 1 y no sube nada; --no-map-update omite el archivo de mapa. Referencia del modo push: todas las opciones y las reglas del archivo de mapa
Haz push de cada valor una sola vez. Un push de un solo secreto guarda el valor como parámetro SecureString y, en el mismo paso, crea envilder.json o le añade la entrada (CLI 0.16 o superior). El primer comando crea el archivo:
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-urlLíneas de resultado de cada comando (se omiten la cabecera y la línea de identidad). Los valores y las rutas están enmascarados:
⭐ 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.jsonenvilder.json ya mapea las tres variables. Solo contiene nombres y rutas, nunca valores:
{
"DB_PASSWORD": "/my-app/dev/db-password",
"API_TOKEN": "/my-app/dev/api-token",
"DATABASE_URL": "/my-app/dev/database-url"
}Ejecuta envilder sin opciones. Pull es el modo por defecto: lee envilder.json y escribe .env en el directorio 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 writtenLos valores se enmascaran: de un valor de más de 10 caracteres solo se muestran los tres últimos, y las rutas se enmascaran igual.
El .env resultante. Envilder entrecomilla DATABASE_URL porque contiene un espacio y un #, y relee el resultado como lo hace dotenv antes de escribir:
DB_PASSWORD=local-dev-password
API_TOKEN=dev-token-7f3a9c21
DATABASE_URL='postgres://app:p@ss w0rd#1@db.internal:5432/app'Si lo ejecutas de nuevo no cambia nada: una línea que ya tiene el valor correcto no se reescribe, y las líneas que el archivo de mapa no declara se conservan.
Varios entornos
Mantén un archivo de mapa por entorno y elígelo con --map. Los nombres de los archivos son tuyos. Este envilder.prod.json mapea las mismas variables a los parámetros de /my-app/prod/:
{
"DB_PASSWORD": "/my-app/prod/db-password",
"API_TOKEN": "/my-app/prod/api-token",
"DATABASE_URL": "/my-app/prod/database-url"
}Pasa --envfile para el archivo que se debe escribir y --profile para el perfil de AWS que puede leer esos parámetros:
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 sustituye a $config.profile del archivo de mapa, y AWS_REGION sigue ganando a la región del perfil.
O prescinde del archivo con envilder exec
Si solo necesitas los valores para arrancar un proceso, envilder exec resuelve el mismo archivo de mapa y arranca tu comando con los parámetros añadidos a su entorno, sin escribir nada en disco:
envilder exec -- node app.jsenvilder: 3 secrets resolved (aws) → node …El resumen va a stderr, así que no se mezcla con la salida de tu comando. El comando lee los valores como variables de entorno normales.
Cuál elegir
- Usa envilder exec cuando arrancas tú el proceso: scripts de npm, tests, migraciones. No queda ningún archivo con secretos en disco, y un parámetro inexistente o ilegible detiene el comando antes de arrancar (código de salida 125).
- Usa un archivo .env cuando una herramienta lee el archivo por ruta o no puedes envolver su comando de arranque: env_file de Docker Compose, una configuración de ejecución del IDE, un framework que carga .env por sí solo. En este modo un parámetro inexistente es solo un aviso, así que mira el resumen (2/3 en lugar de 3/3).
Comparado con un script
La alternativa habitual es un script de shell alrededor de get-parameters-by-path. Para una exportación 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 > .envDónde se queda corto cuando el archivo lo comparte un equipo:
- Es fácil olvidar --with-decryption. Sin esa opción un SecureString vuelve cifrado y el script escribe el texto cifrado.
- La API devuelve como máximo 10 parámetros por página. La AWS CLI sigue las páginas por ti, pero un script basado en un SDK o en una llamada HTTP directa tiene que seguir NextToken por su cuenta.
- Los valores se escriben sin comillas: dotenv relee DATABASE_URL=postgres://app:p@ss w0rd#1@db.internal:5432/app como postgres://app:p@ss w0rd, porque el # empieza un comentario.
- > .env sustituye todo el archivo, así que una línea como LOG_LEVEL=debug se pierde, y los nombres de las variables salen del último segmento de la ruta, de modo que renombrar un parámetro renombra la variable sin avisar.
- Nada en Git dice qué parámetros necesita la aplicación. Con envilder.json el contrato se revisa en una pull request, y una variable sin parámetro aparece como aviso.
El script no necesita Node.js, hace una llamada por página de resultados en lugar de una por variable, y exporta todo lo que hay bajo una ruta sin mapa. Envilder solo lee los nombres del archivo de mapa, con una llamada GetParameter cada uno, así que necesita ssm:GetParameter, no ssm:GetParametersByPath.
Errores habituales
| Mensaje | Solución |
|---|---|
Could not load credentials from any providers | El SDK de AWS no ha encontrado ninguna credencial. Ejecuta aws sso login --profile <nombre>, exporta credenciales o pasa --profile para un perfil con nombre. Código de salida 1, ningún .env escrito. |
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 … | Las credenciales funcionan pero no pueden leer ese parámetro. Permite ssm:GetParameter sobre su ARN, y kms:Decrypt para una clave administrada por el cliente. El archivo .env no se toca. |
secret not found (path: *******************dsn) — skipped | El parámetro no existe en la región de la primera línea de la salida. Comprueba la ruta y la región. El código de salida sigue siendo 0, así que mira el 1/2 del resumen. |
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 file | Renombra la clave en envilder.json. La ejecución se detiene antes de leer ningún parámetro. |
No map file found. Provide --map or create envilder.json in the current directory. | Ejecuta envilder desde el directorio que contiene envilder.json, o pasa --map=ruta/al/archivo.json. |
Todos los mensajes del modo pull están en la referencia del modo pull.
En GitHub Actions
En la CI, inicia la sesión del job en AWS con OIDC y usa la Action de Envilder. Configura Node.js ella misma y, con env-file, escribe el archivo. Sin env-file exporta los valores a los pasos siguientes del job.
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 -dEl archivo va al directorio temporal del runner, fuera del espacio de trabajo, para que los pasos posteriores de build, cache o artifacts no lo recojan. Para evitar el archivo, usa el input command de la Action o envilder exec.