Problema
Muchas veces el proceso de llevar código desde un git push hasta una función Lambda en producción se vuelve manual: se sube el zip, se actualiza la versión y se prueba en la consola. Ese flujo manual introduce latencia, errores de copia y dificulta la trazabilidad. El patrón que se repite es la necesidad de un pipeline que, con cada confirmación en el repositorio, compile, ejecute pruebas unitarias y, si todo pasa, despliegue la nueva versión a AWS Lambda sin almacenar credenciales estáticas en GitHub. La falta de automatización también obliga a crear scripts ad‑hoc que rara vez se versionan, lo que complica la recuperación ante fallos.
Causa
- Credenciales estáticas – Usar Access Keys en los secretos de GitHub obliga a rotarlas manualmente y expone claves en caso de filtración.
- Falta de confianza entre proveedores – GitHub y AWS no comparten automáticamente una identidad, por lo que el workflow necesita permisos explícitos para actuar sobre recursos.
- Política de permisos demasiado amplia – Crear un rol IAM con privilegios de administrador para un solo despliegue genera superficies de ataque innecesarias.
- Separación de etapas – Cuando el pipeline combina compilación, pruebas y despliegue en un solo job, cualquier error en la fase de test impide que el código llegue a producción, pero el diagnóstico suele quedar oculto entre logs de AWS y de GitHub.
- Configuración de OIDC incompleta – OIDC requiere registrar el proveedor, crear una política de confianza y limitar los
audysuba la organización y al repositorio correcto; omitir cualquiera de estos pasos rompe la autenticación.
Solución
Adoptar un pipeline basado en GitHub Actions que utilice OpenID Connect (OIDC) para obtener credenciales temporales en AWS. El flujo genérico es:
- Registrar GitHub como proveedor OIDC en la cuenta AWS.
- Crear un rol IAM con una política que permita
lambda:UpdateFunctionCode(y opcionalmentelambda:PublishVersion) sobre la función objetivo. La política de confianza del rol debe aceptar tokens emitidos por GitHub para la organización y repositorio específicos. - Definir un workflow en
.github/workflows/deploy.ymlcon tres jobs:build,testydeploy. Cada job corre en un runner Linux, usa la acción oficialaws-actions/configure-aws-credentialsconrole-to-assumeapuntando al rol creado. - Mantener los nombres de la función y el bucket S3 como variables de entorno, de modo que el mismo workflow sirva a varios entornos (dev, staging, prod) mediante matrices o
if‑conditions. - Agregar una etapa de verificación que invoque la función recién desplegada con un payload de prueba y compare la respuesta contra un valor esperado; si falla, el job marca el pipeline como error y evita la promoción.
Este enfoque elimina secretos permanentes, limita el alcance del rol a la única acción requerida y mantiene todo el proceso bajo control de versiones.
Alternativas prácticas
- Uso de SAM CLI: empaquetar la función con
sam packageysam deploydentro del jobdeploy. - Deploy a través de CloudFormation: crear o actualizar una stack que contenga la función Lambda; el rol IAM solo necesita permisos de
cloudformation:UpdateStack. - Múltiples entornos con
environmentde GitHub: definir entornosdev,stagingyprody asociar cada uno a un rol distinto.
Cuándo aplicar esta solución
- Frecuencia de cambios: repositorios con despliegues diarios o varias veces al día.
- Equipos distribuidos: cuando varios desarrolladores necesitan desplegar sin compartir claves.
- Regulación de acceso: entornos donde la auditoría de credenciales es obligatoria.
- Errores recurrentes: despliegues que fallan por credenciales expiradas o por permisos excesivos.
No es la mejor opción si la función Lambda se actualiza una vez al mes y el equipo prefiere usar la consola de AWS; la sobrecarga de configurar OIDC puede no justificar el beneficio.
Código
# 1. Registrar GitHub como proveedor OIDC
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
# 2. Crear política que permita actualizar la Lambda
cat > lambda-update-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:UpdateFunctionCode",
"lambda:PublishVersion"
],
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-function"
}
]
}
EOF
aws iam create-policy --policy-name LambdaUpdatePolicy --policy-document file://lambda-update-policy.json
# 3. Crear rol IAM con confianza OIDC
cat > trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"
},
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
}
]
}
EOF
aws iam create-role --role-name GitHubLambdaDeployRole --assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --role-name GitHubLambdaDeployRole --policy-arn arn:aws:iam::123456789012:policy/LambdaUpdatePolicy
# 4. Workflow de GitHub Actions (guárdalo en .github/workflows/deploy.yml)
cat > .github/workflows/deploy.yml <<'EOF'
name: CI/CD Lambda
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
outputs:
artifact: ${{ steps.package.outputs.artifact }}
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Package Lambda
id: package
run: |
zip -r function.zip .
echo "artifact=function.zip" >> $GITHUB_OUTPUT
test:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Install deps
run: npm ci
- name: Run unit tests
run: npm test
deploy:
runs-on: ubuntu-latest
needs: [build, test]
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubLambdaDeployRole
aws-region: us-east-1
- name: Upload artifact to S3
run: |
aws s3 cp ${{ needs.build.outputs.artifact }} s3://my-bucket/function.zip
- name: Update Lambda code
run: |
aws lambda update-function-code \
--function-name my-function \
--s3-bucket my-bucket \
--s3-key function.zip \
--publish
- name: Smoke test
run: |
RESPONSE=$(aws lambda invoke \
--function-name my-function \
--payload '{"test":true}' \
/dev/stdout)
echo "$RESPONSE"
if [[ "$RESPONSE" != *"expectedResult"* ]]; then
echo "Smoke test failed"
exit 1
fi
EOF
Verificación
- Commit a la rama
main. GitHub debe iniciar el workflow y mostrar tres jobs en ejecución. - En la pestaña Actions, verifica que
buildytestfinalicen con ✅. - Cuando
deploytermine, abre la consola de Lambda y revisa la versión publicada; el número de versión debe haber incrementado. - Ejecuta manualmente
aws lambda invokecon el mismo payload usado en el jobSmoke test. La salida debe coincidir con la esperada. - Revisa CloudTrail para confirmar que el rol
GitHubLambdaDeployRolefue asumido medianteAssumeRoleWithWebIdentityy que solo se ejecutóUpdateFunctionCode.
Notas adicionales
- Rotación de roles: si cambias de repositorio o de organización, actualiza la condición
suben la política de confianza; de lo contrario el token será rechazado. - Limitaciones de tamaño: el zip de la Lambda no puede superar 50 MiB (directo) o 250 MiB (a través de S3). Usa
aws lambda update-function-code --zip-file fileb://...solo para paquetes pequeños