Terraform AWS Lambda: Automatiza Funciones Serverless 2025

Terraform AWS Lambda infraestructura como código serverless

Terraform AWS Lambda es la solución definitiva para automatizar el despliegue de funciones serverless en Amazon Web Services mediante infraestructura como código. Esta combinación permite gestionar aplicaciones sin servidor de forma reproducible, escalable y eficiente, eliminando configuraciones manuales propensas a errores.

¿Qué es Terraform AWS Lambda?

AWS Lambda es un servicio de computación serverless que ejecuta código en respuesta a eventos sin necesidad de aprovisionar o administrar servidores. Cuando combinamos Lambda con Terraform, obtenemos una metodología de infraestructura como código (IaC) que permite versionar, reutilizar y compartir configuraciones de funciones serverless.

El recurso aws_lambda_function de HashiCorp permite definir funciones Lambda declarativamente. Esto significa especificar el estado deseado de tu infraestructura y dejar que esta herramienta se encargue de alcanzarlo.

A diferencia de las configuraciones manuales en la consola de AWS, este enfoque ofrece trazabilidad completa mediante control de versiones Git, capacidad de replicar entornos idénticos en desarrollo, staging y producción, y automatización completa del ciclo de vida de las funciones serverless.

Ventajas de usar Terraform AWS Lambda

La automatización con infraestructura como código proporciona beneficios significativos frente al despliegue manual:

  • Reproducibilidad garantizada: Cada despliegue utiliza exactamente la misma configuración, eliminando inconsistencias entre entornos.
  • Versionado completo: Todo cambio queda registrado en Git, permitiendo rollbacks inmediatos y auditorías completas.
  • Reutilización mediante módulos: Crea plantillas reutilizables que estandarizan funciones Lambda en toda tu organización.
  • Reducción de errores humanos: La automatización elimina errores de configuración manual típicos en consolas web.
  • Integración CI/CD nativa: Despliegues automáticos en pipelines de Jenkins, GitHub Actions o GitLab CI.
  • Gestión de dependencias: El módulo oficial terraform-aws-modules/lambda maneja empaquetado, capas y dependencias automáticamente.

Según estadísticas recientes del Terraform Registry, el módulo oficial para Lambda acumula más de 78.9 millones de descargas, convirtiéndolo en uno de los módulos más utilizados del ecosistema AWS.

Configuración básica de Terraform AWS Lambda

Para desplegar una función Lambda básica necesitas cuatro componentes fundamentales: el código de la función empaquetado en ZIP, un rol IAM con permisos de ejecución, la función Lambda propiamente dicha, y opcionalmente CloudWatch Logs para monitoreo.

A continuación se muestra un ejemplo completo de configuración:

# Empaquetar código de la función
data "archive_file" "lambda_zip" {
  type        = "zip"
  source_dir  = "${path.module}/lambda-function"
  output_path = "${path.module}/lambda-function.zip"
}

# Rol IAM para Lambda
resource "aws_iam_role" "lambda_exec" {
  name = "lambda-exec-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "lambda.amazonaws.com"
      }
    }]
  })
}

# Adjuntar política de ejecución básica
resource "aws_iam_role_policy_attachment" "lambda_policy" {
  role       = aws_iam_role.lambda_exec.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# Función Lambda
resource "aws_lambda_function" "api_handler" {
  filename      = data.archive_file.lambda_zip.output_path
  function_name = "api-handler"
  role          = aws_iam_role.lambda_exec.arn
  handler       = "index.handler"
  runtime       = "nodejs20.x"
  timeout       = 30
  memory_size   = 512

  source_code_hash = data.archive_file.lambda_zip.output_base64sha256

  environment {
    variables = {
      ENV = "production"
      LOG_LEVEL = "info"
    }
  }
}

# CloudWatch Logs
resource "aws_cloudwatch_log_group" "lambda_logs" {
  name              = "/aws/lambda/${aws_lambda_function.api_handler.function_name}"
  retention_in_days = 30
}

Este código crea una función Lambda completa con empaquetado automático, permisos IAM correctos y logging en CloudWatch. El atributo source_code_hash asegura que la función se actualice automáticamente cuando el código cambie.

Uso del módulo oficial Terraform AWS Lambda

El módulo terraform-aws-modules/lambda/aws simplifica drásticamente la configuración al gestionar automáticamente el empaquetado, dependencias, permisos y recursos relacionados. Este módulo es mantenido oficialmente por la comunidad y sigue las mejores prácticas de AWS.

Ejemplo con el módulo oficial:

module "lambda_function" {
  source  = "terraform-aws-modules/lambda/aws"
  version = "~> 7.0"

  function_name = "api-processor"
  description   = "Procesa peticiones API en tiempo real"
  handler       = "index.lambda_handler"
  runtime       = "python3.12"

  source_path = "../src/lambda-function"

  # Almacenar en S3 para funciones grandes
  store_on_s3 = true
  s3_bucket   = "my-lambda-builds-bucket"

  # Variables de entorno
  environment_variables = {
    DATABASE_URL = var.database_url
    API_KEY      = var.api_key
  }

  # Configuración de red para acceso VPC
  vpc_subnet_ids         = module.vpc.private_subnets
  vpc_security_group_ids = [aws_security_group.lambda_sg.id]
  attach_network_policy  = true

  # Permisos adicionales
  attach_policy_json = true
  policy_json = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "dynamodb:GetItem",
          "dynamodb:PutItem",
          "dynamodb:Query"
        ]
        Resource = aws_dynamodb_table.data.arn
      }
    ]
  })

  # Triggers permitidos
  allowed_triggers = {
    APIGateway = {
      service    = "apigateway"
      source_arn = "${aws_apigatewayv2_api.main.execution_arn}/*/*"
    }
  }

  tags = {
    Environment = "production"
    ManagedBy   = "Terraform"
  }
}

Esta configuración gestiona automáticamente la creación del paquete ZIP, la subida a S3, los permisos IAM, la configuración de red VPC y los triggers de invocación. El módulo reduce cientos de líneas de código a una configuración clara y mantenible.

Integración con API Gateway mediante Terraform

Para exponer funciones Lambda vía HTTP, la integración con API Gateway es esencial. El siguiente ejemplo muestra cómo crear una API HTTP completa con integración Lambda:

# API Gateway HTTP
resource "aws_apigatewayv2_api" "lambda_api" {
  name          = "lambda-http-api"
  protocol_type = "HTTP"

  cors_configuration {
    allow_origins = ["https://example.com"]
    allow_methods = ["GET", "POST", "PUT", "DELETE"]
    allow_headers = ["content-type", "authorization"]
    max_age       = 300
  }
}

# Stage de producción
resource "aws_apigatewayv2_stage" "production" {
  api_id      = aws_apigatewayv2_api.lambda_api.id
  name        = "production"
  auto_deploy = true

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.api_logs.arn
    format = jsonencode({
      requestId      = "$context.requestId"
      ip             = "$context.identity.sourceIp"
      requestTime    = "$context.requestTime"
      httpMethod     = "$context.httpMethod"
      routeKey       = "$context.routeKey"
      status         = "$context.status"
      protocol       = "$context.protocol"
      responseLength = "$context.responseLength"
    })
  }
}

# Integración Lambda
resource "aws_apigatewayv2_integration" "lambda_integration" {
  api_id             = aws_apigatewayv2_api.lambda_api.id
  integration_type   = "AWS_PROXY"
  integration_uri    = aws_lambda_function.api_handler.invoke_arn
  integration_method = "POST"
  payload_format_version = "2.0"
}

# Ruta de API
resource "aws_apigatewayv2_route" "get_handler" {
  api_id    = aws_apigatewayv2_api.lambda_api.id
  route_key = "GET /api/v1/users"
  target    = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}"
}

# Permiso de invocación
resource "aws_lambda_permission" "api_gateway" {
  statement_id  = "AllowAPIGatewayInvoke"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.api_handler.function_name
  principal     = "apigateway.amazonaws.com"
  source_arn    = "${aws_apigatewayv2_api.lambda_api.execution_arn}/*/*"
}

# CloudWatch para API Gateway
resource "aws_cloudwatch_log_group" "api_logs" {
  name              = "/aws/apigateway/${aws_apigatewayv2_api.lambda_api.name}"
  retention_in_days = 30
}

Esta configuración crea una API HTTP completa con CORS configurado, logging detallado en CloudWatch, y enrutamiento automático a tu función serverless. El uso de payload_format_version = "2.0" proporciona el formato de evento optimizado para HTTP APIs.

Mejores prácticas de seguridad para Terraform AWS Lambda

La seguridad en funciones serverless requiere atención especial a permisos IAM, gestión de secretos y configuración de red:

Principio de mínimo privilegio

Concede únicamente los permisos estrictamente necesarios para cada función:

resource "aws_iam_role_policy" "lambda_dynamodb" {
  name = "lambda-dynamodb-access"
  role = aws_iam_role.lambda_exec.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "dynamodb:GetItem",
          "dynamodb:Query"
        ]
        Resource = "arn:aws:dynamodb:${var.region}:${var.account_id}:table/users"
        Condition = {
          StringEquals = {
            "dynamodb:LeadingKeys" = ["$${aws:username}"]
          }
        }
      }
    ]
  })
}

Gestión segura de secretos

Nunca hardcodees credenciales. Utiliza AWS Secrets Manager o Parameter Store:

# Crear secreto
resource "aws_secretsmanager_secret" "api_key" {
  name = "lambda/api-key"
}

resource "aws_secretsmanager_secret_version" "api_key" {
  secret_id     = aws_secretsmanager_secret.api_key.id
  secret_string = var.api_key
}

# Permisos para leer secreto
resource "aws_iam_role_policy" "secrets_access" {
  name = "secrets-manager-access"
  role = aws_iam_role.lambda_exec.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Action = [
        "secretsmanager:GetSecretValue"
      ]
      Resource = aws_secretsmanager_secret.api_key.arn
    }]
  })
}

# Pasar ARN del secreto a Lambda
resource "aws_lambda_function" "secure_function" {
  # ... otras configuraciones ...

  environment {
    variables = {
      SECRET_ARN = aws_secretsmanager_secret.api_key.arn
    }
  }
}

Aislamiento de red con VPC

Para funciones que acceden a recursos privados, configura VPC correctamente:

module "lambda_in_vpc" {
  source = "terraform-aws-modules/lambda/aws"

  function_name = "private-lambda"
  handler       = "index.handler"
  runtime       = "python3.12"
  source_path   = "../src/private-function"

  # Configuración VPC
  vpc_subnet_ids         = data.aws_subnets.private.ids
  vpc_security_group_ids = [aws_security_group.lambda_sg.id]
  attach_network_policy  = true  # Añade permisos ENI automáticamente
}

# Security Group para Lambda
resource "aws_security_group" "lambda_sg" {
  name        = "lambda-sg"
  description = "Security group for Lambda functions"
  vpc_id      = data.aws_vpc.main.id

  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "HTTPS outbound"
  }

  egress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.rds_sg.id]
    description     = "PostgreSQL access"
  }
}

Recuerda que las funciones en VPC requieren NAT Gateway o VPC Endpoints para acceder a servicios AWS públicos. Si necesitas más información sobre automatización de infraestructura, revisa nuestra guía de Docker Compose para arquitecturas de microservicios.

Gestión de capas Lambda con Terraform

Las capas Lambda permiten compartir código, dependencias y librerías entre múltiples funciones, reduciendo el tamaño de los paquetes y facilitando el mantenimiento:

# Crear capa con dependencias comunes
module "lambda_layer" {
  source  = "terraform-aws-modules/lambda/aws"
  version = "~> 7.0"

  create_layer = true
  layer_name   = "common-dependencies"
  description  = "Librerías compartidas para funciones Python"

  compatible_runtimes = ["python3.12", "python3.11"]

  source_path = [
    {
      path = "../layers/python"
      pip_requirements = "../layers/requirements.txt"
      prefix_in_zip = "python"
    }
  ]

  store_on_s3 = true
  s3_bucket   = "lambda-layers-bucket"
}

# Usar capa en función Lambda
module "lambda_with_layer" {
  source  = "terraform-aws-modules/lambda/aws"

  function_name = "function-with-dependencies"
  handler       = "index.handler"
  runtime       = "python3.12"
  source_path   = "../src/function"

  layers = [
    module.lambda_layer.lambda_layer_arn
  ]
}

Las capas pueden contener hasta 250 MB de código comprimido y se cargan en /opt en el entorno de ejecución. Esto es especialmente útil para frameworks pesados como TensorFlow, pandas o bibliotecas de procesamiento de imágenes.

Monitoreo y observabilidad con CloudWatch

El monitoreo efectivo es crítico para aplicaciones serverless en producción. CloudWatch proporciona métricas, logs y alarmas integradas:

# Alarma para errores de Lambda
resource "aws_cloudwatch_metric_alarm" "lambda_errors" {
  alarm_name          = "lambda-errors-${aws_lambda_function.api_handler.function_name}"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name         = "Errors"
  namespace           = "AWS/Lambda"
  period              = 300
  statistic           = "Sum"
  threshold           = 5
  alarm_description   = "Triggered when Lambda errors exceed threshold"
  treat_missing_data  = "notBreaching"

  dimensions = {
    FunctionName = aws_lambda_function.api_handler.function_name
  }

  alarm_actions = [aws_sns_topic.alerts.arn]
}

# Alarma para duración excesiva
resource "aws_cloudwatch_metric_alarm" "lambda_duration" {
  alarm_name          = "lambda-duration-${aws_lambda_function.api_handler.function_name}"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 3
  metric_name         = "Duration"
  namespace           = "AWS/Lambda"
  period              = 60
  statistic           = "Average"
  threshold           = 5000  # 5 segundos
  alarm_description   = "Triggered when average duration exceeds 5 seconds"

  dimensions = {
    FunctionName = aws_lambda_function.api_handler.function_name
  }

  alarm_actions = [aws_sns_topic.alerts.arn]
}

# Alarma para invocaciones concurrentes
resource "aws_cloudwatch_metric_alarm" "lambda_throttles" {
  alarm_name          = "lambda-throttles-${aws_lambda_function.api_handler.function_name}"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name         = "Throttles"
  namespace           = "AWS/Lambda"
  period              = 60
  statistic           = "Sum"
  threshold           = 10
  alarm_description   = "Triggered when function is throttled"

  dimensions = {
    FunctionName = aws_lambda_function.api_handler.function_name
  }

  alarm_actions = [aws_sns_topic.alerts.arn]
}

# SNS Topic para notificaciones
resource "aws_sns_topic" "alerts" {
  name = "lambda-alerts"
}

resource "aws_sns_topic_subscription" "email" {
  topic_arn = aws_sns_topic.alerts.arn
  protocol  = "email"
  endpoint  = "[email protected]"
}

Para monitoreo avanzado de infraestructura, considera herramientas como Uptime Kuma que complementan las métricas nativas de CloudWatch con dashboards personalizados.

Estrategias de despliegue y versionado

Lambda soporta versionado y alias para despliegues blue-green y canary:

# Publicar versión inmutable
resource "aws_lambda_function" "api_handler" {
  # ... configuración base ...

  publish = true  # Crea nueva versión en cada deploy
}

# Alias de producción apuntando a versión estable
resource "aws_lambda_alias" "production" {
  name             = "production"
  description      = "Alias de producción estable"
  function_name    = aws_lambda_function.api_handler.function_name
  function_version = aws_lambda_function.api_handler.version
}

# Despliegue canary con tráfico dividido
resource "aws_lambda_alias" "canary" {
  name             = "canary"
  description      = "Despliegue canary con 10% de tráfico"
  function_name    = aws_lambda_function.api_handler.function_name
  function_version = aws_lambda_function.api_handler.version

  routing_config {
    additional_version_weights = {
      (aws_lambda_function.api_handler.version - 1) = 0.9  # 90% a versión anterior
    }
  }
}

# API Gateway usa alias para enrutamiento
resource "aws_lambda_permission" "api_gateway_production" {
  statement_id  = "AllowAPIGatewayInvokeProduction"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.api_handler.function_name
  principal     = "apigateway.amazonaws.com"
  qualifier     = aws_lambda_alias.production.name
  source_arn    = "${aws_apigatewayv2_api.lambda_api.execution_arn}/*/*"
}

Este enfoque permite rollbacks instantáneos simplemente actualizando el alias sin necesidad de redesplegar código.

Automatización CI/CD para despliegues Lambda

Integrar este flujo en pipelines CI/CD garantiza despliegues consistentes y auditables. Ejemplo con GitHub Actions:

name: Deploy Lambda with Terraform

on:
  push:
    branches: [main]
    paths:
      - 'terraform/**'
      - 'src/**'

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.7.0

      - name: Terraform Init
        working-directory: terraform
        run: terraform init

      - name: Terraform Validate
        working-directory: terraform
        run: terraform validate

      - name: Terraform Plan
        working-directory: terraform
        run: terraform plan -out=tfplan

      - name: Terraform Apply
        working-directory: terraform
        run: terraform apply -auto-approve tfplan

      - name: Run Integration Tests
        run: |
          pip install pytest requests
          pytest tests/integration/

Este pipeline ejecuta validación, planificación, despliegue y pruebas automáticas en cada push a la rama principal, asegurando calidad y consistencia en cada cambio de infraestructura.

Preguntas frecuentes (FAQ)

¿Cómo gestiono dependencias de Python en funciones Lambda con Terraform?

El módulo oficial terraform-aws-modules/lambda/aws gestiona automáticamente dependencias mediante el parámetro source_path con pip_requirements. Especifica un archivo requirements.txt y el módulo instalará las dependencias localmente o en Docker antes de empaquetar. Para dependencias compartidas entre múltiples funciones, utiliza capas Lambda.

¿Cuál es la diferencia entre aws_lambda_function y el módulo terraform-aws-modules/lambda/aws?

El recurso aws_lambda_function es el recurso nativo que requiere gestión manual de empaquetado, permisos y recursos relacionados. El módulo terraform-aws-modules/lambda/aws es una abstracción de alto nivel que automatiza empaquetado, dependencias, capas, permisos IAM y triggers. El módulo reduce drásticamente la complejidad y sigue mejores prácticas de AWS automáticamente.

¿Cómo implemento blue-green deployments en Lambda mediante Terraform?

Utiliza versionado de Lambda con alias y tráfico dividido. Configura publish = true para crear versiones inmutables, crea alias apuntando a versiones específicas, y usa routing_config para dividir tráfico entre versiones. API Gateway debe invocar el alias en lugar de la función directamente. Esto permite despliegues canary con porcentajes de tráfico configurables y rollbacks instantáneos.

¿Necesito VPC para mis funciones Lambda gestionadas con esta herramienta?

Solo si tus funciones necesitan acceder a recursos privados como bases de datos RDS, clusters ElastiCache o servicios internos. Las funciones sin VPC tienen acceso directo a Internet y servicios AWS públicos con menor latencia de arranque en frío. Funciones en VPC requieren ENIs que añaden latencia inicial y necesitan NAT Gateway o VPC Endpoints para servicios AWS públicos.

¿Cómo gestiono secretos de forma segura en funciones Lambda con Terraform?

Utiliza AWS Secrets Manager o Systems Manager Parameter Store para almacenar secretos, nunca variables de entorno o código. Concede permisos IAM para secretsmanager:GetSecretValue específicamente al ARN del secreto. Pasa el ARN del secreto como variable de entorno y recupera el valor en runtime desde el código de la función. Rota secretos periódicamente usando rotación automática de Secrets Manager.

Conclusión

Terraform AWS Lambda combina la potencia del serverless computing con las ventajas de infraestructura como código, permitiendo despliegues reproducibles, escalables y auditables. Al automatizar el ciclo de vida completo de funciones Lambda mediante declaraciones de código, eliminas errores manuales, mejoras la consistencia entre entornos y habilitas flujos CI/CD robustos.

El módulo oficial terraform-aws-modules/lambda/aws simplifica drásticamente la gestión de funciones serverless al manejar automáticamente empaquetado, dependencias, permisos y recursos relacionados. Con más de 78.9 millones de descargas, este módulo representa el estándar de facto para gestionar Lambda mediante infraestructura como código en AWS.

Implementa las mejores prácticas descritas: principio de mínimo privilegio en IAM, gestión segura de secretos con Secrets Manager, monitoreo proactivo con CloudWatch Alarms, y estrategias de despliegue blue-green con alias y versionado. Estas prácticas garantizan aplicaciones serverless seguras, observables y resilientes en producción.

Comienza hoy mismo automatizando tus despliegues Lambda y experimenta los beneficios de la infraestructura declarativa. Para complementar tu arquitectura serverless, explora herramientas de seguridad como Vaultwarden para gestión de credenciales en tu equipo de desarrollo.

Avatar

Por Mid

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x