Skip to content

Triggering Lambda Functions during CloudFormation deployment with an EventBridge Rule.

4 minute read
Content level: Intermediate
0

A CloudFormation pattern that uses EventBridge Rule to automatically execute Lambda Function during deployment, preventing stack failures and timeouts that may occur with custom resources.

Introduction

When deploying AWS Lambda functions with CloudFormation, sometimes its necessary to execute function right after it is created. While CloudFormation custom resources can achieve this, improper configuration may cause the stack to remain in an extended execution state. This article introduces a alternative approach using EventBridge, which avoids CloudFormation hangs caused by failed responses.

Solution Overview

This pattern leverages CloudFormation's own events to trigger Lambda execution at the optimal moment. The solution uses EventBridge to listen for CloudFormation resource creation events and automatically invoke Lambda functions when specific resources reach the CREATE_COMPLETE status.

AWSTemplateFormatVersion: '2010-09-09'
Description: Auto-trigger Lambda after CloudFormation deployment

Resources:
  # Your Lambda function
  MyLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: |
          import json
          def lambda_handler(event, context):
              print("Initialization logic running...")
              # Your initialization code here
              return {
                  'statusCode': 200,
                  'body': json.dumps('Initialization completed')
              }
      Handler: index.lambda_handler
      Runtime: python3.10
      Role: !GetAtt LambdaExecutionRole.Arn
      Timeout: 300

  # IAM role for Lambda
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  # Permission for EventBridge to invoke Lambda
  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref MyLambdaFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt TriggerRule.Arn

  # EventBridge rule that triggers after Lambda permission is created
  TriggerRule:
    Type: AWS::Events::Rule
    Properties:
      Description: "Trigger Lambda after successful deployment"
      EventBusName: default
      EventPattern:
        source:
          - "aws.cloudformation"
        detail:
          logical-resource-id:
            - LambdaInvokePermission
          status-details:
            status:
              - CREATE_COMPLETE
      Targets:
        - Arn: !GetAtt MyLambdaFunction.Arn
          Id: InitializationTrigger

Implementation Details

The solution works through the following sequence:

  1. CloudFormation deployment: Your Lambda function and related resources are deployed
  2. EventBridge monitoring: An EventBridge rule listens for CloudFormation events on the default event bus
  3. Trigger condition: When the LambdaInvokePermission resource reaches CREATE_COMPLETE status
  4. Automatic execution: Lambda function executes automatically with your initialization logic

Benefits

This EventBridge pattern provides several advantages over traditional approaches:

Prevents CloudFormation Hanging: Unlike custom resources, if your Lambda fails or doesn't respond correctly, CloudFormation won't hang waiting for a response. The stack deployment completes successfully regardless of Lambda execution results.

Optimal Timing: The trigger fires exactly when all necessary permissions and resources are in place, ensuring your Lambda can access everything it needs.

Single Execution: The pattern naturally ensures one-time execution per deployment, preventing duplicate initialization.

Decoupled Architecture: Lambda execution is separate from stack deployment, making debugging easier and reducing deployment complexity.

Use Cases

Database Initialization

def lambda_handler(event, context):
    # Initialize database tables
    # Seed initial data
    # Set up indexes
    return {'statusCode': 200, 'body': 'Database initialized'}

Configuration Setup

def lambda_handler(event, context):
    # Configure external services
    # Set up API keys in Parameter Store
    # Initialize monitoring dashboards
    return {'statusCode': 200, 'body': 'Configuration completed'}

Data Migration

def lambda_handler(event, context):
    # Migrate existing data
    # Transform data formats
    # Validate data integrity
    return {'statusCode': 200, 'body': 'Migration completed'}

Advanced Implementation

For multi-account architectures, you can trigger Lambda functions in different accounts:

CrossAccountTriggerRule:
  Type: AWS::Events::Rule
  Properties:
    EventPattern:
      source:
        - "aws.cloudformation"
      detail:
        logical-resource-id:
          - MyResource
        status-details:
          status:
            - CREATE_COMPLETE
    Targets:
      - Arn: !Sub "arn:aws:lambda:${AWS::Region}:${TargetAccountId}:function:${TargetFunctionName}"
        Id: CrossAccountTrigger

Monitoring and Debugging

Add CloudWatch logging to track execution:

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.info(f"Triggered by CloudFormation event: {json.dumps(event)}")
    
    try:
        # Your initialization logic
        logger.info("Initialization completed successfully")
        return {'statusCode': 200, 'body': 'Success'}
    except Exception as e:
        logger.error(f"Initialization failed: {str(e)}")
        # Note: CloudFormation won't fail even if this Lambda fails
        return {'statusCode': 500, 'body': f'Error: {str(e)}'}

Best Practices

When implementing this pattern, consider the following recommendations:

  • Choose the right trigger resource: Use a resource that's created after your Lambda and its dependencies
  • Add proper IAM permissions: Ensure your Lambda has all necessary permissions for initialization tasks
  • Implement idempotency: Make your initialization logic safe to run multiple times
  • Add comprehensive logging: CloudWatch logs are essential for debugging
  • Consider timeout limits: Set appropriate timeout values for your initialization tasks

Conclusion

This EventBridge pattern provides a robust, scalable way to auto-trigger Lambda functions after CloudFormation deployment. It eliminates the complexity of custom resources while ensuring your initialization logic runs at exactly the right moment, without risking CloudFormation stack failures.

The pattern is particularly valuable for:

  • Database initialization and seeding
  • Configuration setup and validation
  • Data migration and transformation
  • External service integration
  • Monitoring and alerting setup

By leveraging CloudFormation's own event system, you get a self-contained, reliable initialization mechanism that scales with your infrastructure needs.