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