Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Lambda management with ecspresso and Terraform

Lambda management with ecspresso and Terraform

Serverless Meetup Tokyo #21
Aug 6, 2025

https://serverless.connpass.com/event/364488/

Avatar for Michael H. Oshita

Michael H. Oshita

August 06, 2025
Tweet

More Decks by Michael H. Oshita

Other Decks in Technology

Transcript

  1. Lambda management with ecspresso and Terraform @ijin Aug 6, 2025

    Serverless Meetup Tokyo #21 #serverlesstokyo #serverlessjp
  2. AGENDA • About me • Lambda Deployments • App vs

    Infra • Lambroll con fi gs • Jsonnet
  3. Michael H. Oshita (@ijin) • Michael H. Oshita • Japanese

    American • ~20 Years in Japan • Cloud Architect • JAWS-UG Yokohama & Serverless Community & AI Coding Dojo • https://ijin.github.io/
  4. Deploy with awscli # Create deployment package zip -r function.zip

    lambda_function.py # Update function code aws lambda update-function-code \ --function-name my-function \ --zip-file fileb://function.zip # Update function configuration aws lambda update-function-configuration \ --function-name my-function \ --runtime python3.13 \ --handler lambda_function.handler \ --role arn:aws:iam::123456789012:role/lambda-role
  5. Deploy with serverless framework # serverless.yml service: my-lambda provider: name:

    aws vpc: securityGroupIds: ['sg-12345'] subnetIds: ['subnet-12345'] environment: DYNAMODB_TABLE: ${env:DYNAMODB_TABLE} logs: restApi: true iam: role: statements: - Effect: Allow Action: - dynamodb:Query - dynamodb:GetItem - dynamodb:PutItem Resource: - arn:aws:dynamodb:*:*:table/users - arn:aws:dynamodb:*:*:table/users/index/* - Effect: Allow Action: logs:CreateLogGroup Resource: arn:aws:logs:*:*:log-group:/aws/lambda/* functions: processor: handler: handler.process logRetentionInDays: 14 sls deploy
  6. Deploy with aws sam # template.yaml AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31

    Resources: ProcessorRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole Policies: - PolicyName: DynamoDBAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - dynamodb:Query - dynamodb:GetItem - dynamodb:PutItem Resource: - !Sub 'arn:aws:dynamodb:*:*:table/${DynamoDBTable}' - !Sub 'arn:aws:dynamodb:*:*:table/${DynamoDBTable}/index/*' - Effect: Allow Action: logs:CreateLogGroup Resource: arn:aws:logs:*:*:log-group:/aws/lambda/* ProcessorFunction: Type: AWS::Serverless::Function Properties: CodeUri: src/ Handler: handler.process Role: !GetAtt LambdaExecutionRole.Arn VpcConfig: SecurityGroupIds: - sg-12345 SubnetIds: - subnet-12345 - subnet-67890 Environment: Variables: DYNAMODB_TABLE: !Ref DynamoDBTable DYNAMODB_GSI: !Ref DynamoDBGSI Events: DynamoDBStream: Type: DynamoDB Properties: Stream: !GetAtt ProcessorTable.StreamArn StartingPosition: TRIM_HORIZON BatchSize: 10 sam build && sam deploy
  7. Deploy with terraform resource "aws_iam_role" "lambda_role" { .... } resource

    "aws_iam_role_policy_attachment" "lambda_vpc" { .... } resource "aws_iam_role_policy" "dynamodb_access" { .... } resource "aws_dynamodb_table" "users" { .... } resource "aws_cloudwatch_log_group" "processor_logs" { .... } data "archive_file" "lambda_zip" { type = "zip" source_dir = "${path.module}/src" output_path = "${path.module}/processor.zip" } resource "aws_lambda_function" "processor" { filename = data.archive_file.lambda_zip.output_path function_name = "processor" role = aws_iam_role.lambda_role.arn handler = "handler.process" runtime = “python3.13" source_code_hash = data.archive_file.lambda_zip.output_base64s vpc_config { subnet_ids = ["subnet-12345", "subnet-67890"] security_group_ids = ["sg-12345"] } logging_config { log_format = "Text" log_group = aws_cloudwatch_log_group.processor_logs.name } } terraform apply
  8. App vs Infra - Different release velocity - Different lifecycle

    - Separation of responsibilities - Contain blast radius
  9. Lambda deployment 1. terragrunt run-all apply 2. lambroll deploy --function

    function.jsonnet -- src="app" --option=option.jsonnet --env fi le=../ global.env --env fi le=vars/common.env — env fi le=vars/dev.env
  10. Jsonnet { cpu: {{ must_env `cpu` }} memory: std.parseInt(‘{{ must_env

    `memory` }}’) } ❌ Templates only worked for strings…
  11. lambroll deploy…. lambroll deploy --function function.jsonnet --src="app" \ —env fi

    le=../global.env \ —env fi le=vars/common.env \ —env fi le=vars/${ENV}.env \ —pre fi xed-tfstate=“vpc_=s3://pd-${ENV}-tfstate/${ENV}/vpc/tf.tfstate” \ —pre fi xed-tfstate=“logs_=s3://pd-${ENV}-tfstate/${APP}/logs/tf.tfstate" \ —pre fi xed-tfstate=“ecr_=s3://pd-${ENV}-tfstate/${APP}/ecr/tf.tfstate” \ —pre fi xed-tfstate=“app_resources_=s3://pd-${ENV}-tfstate/${ENV}/ app_resources/terraform.tfstate"
  12. lambroll deploy…. lambroll deploy --function function.jsonnet --src="app" \ —env fi

    le=../global.env \ —env fi le=vars/common.env \ —env fi le=vars/${ENV}.env \ —pre fi xed-tfstate=“vpc_=s3://pd-${ENV}-tfstate/${ENV}/vpc/tf.tfstate” \ —pre fi xed-tfstate=“logs_=s3://pd-${ENV}-tfstate/${APP}/logs/tf.tfstate" \ —pre fi xed-tfstate=“ecr_=s3://pd-${ENV}-tfstate/${APP}/ecr/tf.tfstate” \ —pre fi xed-tfstate=“app_resources_=s3://pd-${ENV}-tfstate/${ENV}/ app_resources/terraform.tfstate" 😳 No equivalent of ecspresso.yml