Python Docker Container #
Project Folder #
# Create project folder
TF_PROJECT_NAME=python-email-validator
mkdir $TF_PROJECT_NAME && cd $TF_PROJECT_NAME
The file and folder structure looks like this:
python-email-validator
├── app.py
├── Dockerfile
Dockerfile #
- Dockerfile
# Lambda base image for Docker
FROM public.ecr.aws/lambda/python:latest
# Copy the application code into the container
COPY app.py .
# Set the Lambda handler (should match function name in app.py)
CMD ["app.validate"]
Python App #
- app.py
import json
import re
def validate(event, context):
    event_body = json.loads(event['body'])
    email_regex = re.compile('^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
    matches = email_regex.match(event_body['email']) != None
    response = {
        'statusCode': 200,
        'body': json.dumps({ 'result': matches })
    }
    return response
Build Docker Image #
# Build container image
docker build -t python-email-validator .
 
CloudFormation Configuration #
Project Folder #
# Create project folder
TF_PROJECT_NAME=aws-ecr-lambda-cloudformation
mkdir $TF_PROJECT_NAME && cd $TF_PROJECT_NAME
The file and folder structure looks like this:
aws-ecr-lambda-cloudformation
├── ecr-cloudformation.yaml
└── lambda-cloudformation.yaml
ECR Configuration #
- ecr-cloudformation.yaml
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  RepositoryName:
    Type: String
    Default: lambda-docker-example
    Description: "Name of the ECR repository"
Resources:
  ECRRepository:
    Type: AWS::ECR::Repository
    Properties:
      EmptyOnDelete: true
      EncryptionConfiguration:
        EncryptionType: AES256
      ImageScanningConfiguration:
        ScanOnPush: true
      ImageTagMutability: MUTABLE
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [
              {
                "rulePriority": 1,
                "description": "Expire untagged images older than 14 days",
                "selection": {
                  "tagStatus": "untagged",
                  "countType": "sinceImagePushed",
                  "countUnit": "days",
                  "countNumber": 14
                },
                "action": {
                  "type": "expire"
                }
              }
            ]
          }          
      RepositoryName: !Ref RepositoryName
Outputs:
  ECRRepositoryUri:
    Value: !GetAtt ECRRepository.RepositoryUri
    Description: "ECR repository URI"
LambdaConfiguration #
- lambda-cloudformation.yaml
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  RepositoryName:
    Type: String
    Default: lambda-docker-example
    Description: "Name of the ECR repository"
Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: jkw-lambda-example
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: 
                - lambda.amazonaws.com
            Action: 
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: lambda-email-validator
      PackageType: Image
      Code:
        ImageUri: 
          !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${RepositoryName}:latest"
      Role: !GetAtt LambdaExecutionRole.Arn
      Timeout: 15
      MemorySize: 128  # Default size
Outputs:
  LambdaFunctionArn:
    Value: !GetAtt LambdaFunction.Arn
    Description: "Lambda function ARN"
 
ECR #
Create Stack #
# Create the ECR stack
aws cloudformation create-stack \
    --stack-name ecr-stack \
    --template-body file://ecr-cloudformation.yaml \
    --region eu-central-1
# Shell output:
{
    "StackId": "arn:aws:cloudformation:eu-central-1:073526172187:stack/ecr-stack/9954b650-e543-11ef-b3f0-023c814d0f7b"
}
Monitor the Deployment #
Wait till “StackStatus” changes to “CREATE_COMPLETE”:
# Monitor the deployment
aws cloudformation describe-stacks \
  --stack-name ecr-stack \
  --region eu-central-1 \
  --no-cli-pager
# Shell output:
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:eu-central-1:073526172187:stack/ecr-stack/9954b650-e543-11ef-b3f0-023c814d0f7b",
            "StackName": "ecr-stack",
            "Parameters": [
                {
                    "ParameterKey": "RepositoryName",
                    "ParameterValue": "lambda-docker-example"
                }
            ],
            "CreationTime": "2025-02-07T11:06:37.018000+00:00",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Outputs": [
                {
                    "OutputKey": "ECRRepositoryUri",
                    "OutputValue": "073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example",
                    "Description": "URI of the created ECR repository"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}
Outputs #
Verify the ECR URL:
# Verify the ECR URL:
aws cloudformation describe-stacks \
    --stack-name ecr-stack \
    --query "Stacks[0].Outputs" \
    --region eu-central-1
# Shell output:
[
    {
        "OutputKey": "ECRRepositoryUri",
        "OutputValue": "073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example",
        "Description": "URI of the created ECR repository"
    }
]
Docker Image #
Docker Login #
Login to the ECR repository:
# Retrieve an authentication token and authenticate the Docker client
aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin 073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example
# Shell output:
WARNING! Your password will be stored unencrypted in /home/ubuntu/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credential-stores
Login Succeeded
Tag the Image for AWS ECR #
Tag the previously created Docker image to associate it with the ECR repository:
# Tag the local Docker image
docker tag python-email-validator:latest 073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example:latest
Verify the image:
# List Docker images
docker images
# Shell output:
REPOSITORY                                                              TAG       IMAGE ID       CREATED         SIZE
073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example   latest    5dae1b122e72   6 minutes ago   515MB
python-email-validator                                                  latest    5dae1b122e72   6 minutes ago   515MB
Push the Image to ECR #
# Push the image to ECR
docker push 073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example:latest
# Shell output:
The push refers to repository [073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example]
cc88962b2437: Pushed
d40d1b6fe31d: Pushed
2195ebbc1042: Pushed
0091b0f618e4: Pushed
4225886892d5: Pushed
393348208dca: Pushed
6281cd02ff74: Pushed
latest: digest: sha256:644a8c8a754632b76c141299dfd546c4db846c172f9b3cb849f3855a2b5651f9 size: 1785
  
Lambda Function #
Create Stack #
# Create the Lambda stack
aws cloudformation create-stack \
    --stack-name lambda-stack \
    --template-body file://lambda-cloudformation.yaml \
    --capabilities CAPABILITY_NAMED_IAM \
    --region eu-central-1
# Shell output:
{
    "StackId": "arn:aws:cloudformation:eu-central-1:073526172187:stack/lambda-stack/7f7f9820-e544-11ef-8a1a-0a91f6b8750b"
}
Monitor the Deployment #
Wait till “StackStatus” changes to “CREATE_COMPLETE”:
# Monitor the deployment
aws cloudformation describe-stacks \
  --stack-name lambda-stack \
  --region eu-central-1 \
  --no-cli-pager
# Shell output:
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:eu-central-1:073526172187:stack/lambda-stack/7f7f9820-e544-11ef-8a1a-0a91f6b8750b",
            "StackName": "lambda-stack",
            "Parameters": [
                {
                    "ParameterKey": "RepositoryName",
                    "ParameterValue": "lambda-docker-example"
                }
            ],
            "CreationTime": "2025-02-07T11:13:03.213000+00:00",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Capabilities": [
                "CAPABILITY_NAMED_IAM"
            ],
            "Outputs": [
                {
                    "OutputKey": "LambdaFunctionArn",
                    "OutputValue": "arn:aws:lambda:eu-central-1:073526172187:function:lambda-email-validator",
                    "Description": "Lambda function ARN"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}
Test Lambda Function #
Example Payload #
# Install jq for encoding
Install jq
# Create example payload files
echo '{"body": "{\"email\": \"test@example.com\"}"}' > payload1.json
echo '{"body": "{\"email\": \"test.example.com\"}"}' > payload2.json
# Encode payload files
cat payload1.json | base64 | tr -d '\n' > payload1_base64.json
cat payload2.json | base64 | tr -d '\n' > payload2_base64.json
Invoke Lambda Function #
# Invoke the Lambda function
aws lambda invoke --function-name lambda-email-validator \
  --region eu-central-1 \
  --payload file://payload1_base64.json\
  response1.json
# Invoke the Lambda function
aws lambda invoke --function-name lambda-email-validator \
  --region eu-central-1 \
  --payload file://payload2_base64.json \
  response2.json
# Shell output:
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
Verify the output:
# Cat the response files:
cat response1.json
cat response2.json
# Shell output:
{"statusCode": 200, "body": "{\"result\": true}"}
{"statusCode": 200, "body": "{\"result\": false}"}
 
Cleanup #
# Delete the Lambda stack
aws cloudformation delete-stack \
  --stack-name lambda-stack \
  --region eu-central-1
# Delete the ECR stack
aws cloudformation delete-stack \
  --stack-name ecr-stack \
  --region eu-central-1