# Introduction In this guide, I’ll walk you through how to build a Lambda function using a Docker image, push it to ECR, and deploy it with Terraform — all step by step! # Structure {%preview https://github.com/chungchihhan/terraform-practice-aws.git %} ```markdown lambda-ecr/ │ ├── deployment/ │ ├── .terraform/ │ ├── .gitignore │ ├── .terraform.lock.hcl │ ├── main.tf │ ├── providers.tf │ ├── terraform.tfstate │ ├── terraform.tfstate.backup │ └── variables.tf │ ├── src/ │ ├── Dockerfile │ ├── lambda_function.py │ ├── push_to_ecr.sh │ └── requirements.txt │ └── README.md ``` > [!Tip] > To checkout the account > ```bash > aws sts get-caller-identity > ``` </aside> # Build your lambda function image ### Navigate to the `src` folder ```bash cd src ``` ### Create a `lambda_function.py` in the folder ```python import sys def **handler**(event, context): return 'Hello from AWS Lambda using Python ' + sys.version + '!' ``` ### Create a `requirements.txt` in the folder ``` boto3 ``` ### Create a `DockerFile` in the folder ```docker FROM public.ecr.aws/lambda/python:3.12 COPY requirements.txt ${LAMBDA_TASK_ROOT} RUN pip install -r requirements.txt COPY lambda_function.py ${LAMBDA_TASK_ROOT} CMD [ "lambda_function.**lambda_handler**" ] ``` ### Build the image ```bash docker build --platform linux/amd64 -t <image name>:<image tag> . ``` > [!Warning] > 此命令會指定 `--platform linux/amd64` 選項,確保無論建置機器的架構為何,您的容器都與 Lambda 執行環境相容。如果您打算使用ARM64指令集架構建立 Lambda 函數,請務必將命令變更為使用該`--platform linux/arm64`選項。 </aside> ### Run the `push_to_ecr.sh` This file helps you log in first, and then create a ECR repository for the tagged image to push. > [!Important] > Remember to change the variables and profile ! ```powershell #!/bin/bash # Define variables accountId="381492289756" region="ap-northeast-1" repositoryName="my-python-lambda-repo" imageName="docker-image" imageTag="test" # Login to ECR aws ecr get-login-password --region "$region" | docker login --username AWS --password-stdin "$accountId.dkr.ecr.$region.amazonaws.com" # Create ECR repository aws ecr create-repository --repository-name "$repositoryName" --region "$region" # Tag Docker image docker tag "$imageName:$imageTag" "$accountId.dkr.ecr.$region.amazonaws.com/$repositoryName:latest" # Push image to ECR docker push "$accountId.dkr.ecr.$region.amazonaws.com/$repositoryName:latest" ``` ```powershell #!/bin/bash # Define variables accountId="070576557102" region="ap-northeast-1" repositoryName="dev-surveycake-repo" imageName="surveycake" imageTag="dev" # Specified the credentail profile profile="rich-liu" # Function to check if the repository exists repository_exists() { aws ecr describe-repositories --repository-names "$repositoryName" --region "$region" --profile "$profile" > /dev/null 2>&1 return $? # Return the exit status of the command } # Login to ECR aws ecr get-login-password --region "$region" --profile "$profile" | docker login --username AWS --password-stdin "$accountId.dkr.ecr.$region.amazonaws.com" # Check if repository exists if repository_exists; then echo "Repository $repositoryName already exists, skipping creation." else echo "Repository $repositoryName does not exist, creating repository." aws ecr create-repository --repository-name "$repositoryName" --region "$region" --profile "$profile" # Build Docker image (uncomment if you want to rebuild the image every time) # docker build -t "$imageName:$imageTag" . # Tag Docker image docker tag "$imageName:$imageTag" "$accountId.dkr.ecr.$region.amazonaws.com/$repositoryName:latest" # Push image to ECR docker push "$accountId.dkr.ecr.$region.amazonaws.com/$repositoryName:latest" ``` ```bash ./push_to_ecr.sh ``` > [!Warning] > If you got a permission denied, you can check out the permission and make the script executable. > ```bash > ls -l > ``` > ```bash > chmod +x push_to_ecr.sh > ``` </aside> # Deploy the lambda function with the image ### Navigate to the `deployment` folder ```bash cd deployment ``` ### Set up the `providers.tf` , `main.tf` , `variables.tf` - providers.tf ``` terraform { required_providers { aws = { source = "hashicorp/aws" version = "5.68.0" } } } provider "aws" { region = "ap-northeast-1" } ``` - main.tf ``` data "aws_iam_policy_document" "iam_policy" { statement { effect = "Allow" principals { type = "Service" identifiers = ["lambda.amazonaws.com"] } actions = ["sts:AssumeRole"] } } resource "aws_iam_role" "iam_role" { name = "${var.lambda_function_name}-${var.env_name}-role" assume_role_policy = data.aws_iam_policy_document.iam_policy.json } resource "aws_cloudwatch_log_group" "cloudwatch_log_group" { name = "/aws/lambda/${var.lambda_function_name}" retention_in_days = 14 } data "aws_iam_policy_document" "lambda_logging" { statement { effect = "Allow" actions = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ] resources = ["arn:aws:logs:*:*:*"] } } resource "aws_iam_policy" "lambda_logging" { name = "lambda_logging" path = "/" description = "IAM policy for logging from a lambda" policy = data.aws_iam_policy_document.lambda_logging.json } resource "aws_iam_role_policy_attachment" "lambda_logs" { role = aws_iam_role.iam_role.name policy_arn = aws_iam_policy.lambda_logging.arn } resource "aws_lambda_function" "lambda_function" { function_name = "${var.lambda_function_name}-${var.env_name}" timeout = 5 # seconds image_uri = var.ecr_image_uri package_type = "Image" role = aws_iam_role.iam_role.arn environment { variables = { ENVIRONMENT = var.env_name } } depends_on = [aws_iam_role_policy_attachment.lambda_logs, aws_cloudwatch_log_group.cloudwatch_log_group] } ``` - variables.tf ``` variable "env_name" { description = "Environment name" type = string default = "dev" } variable "lambda_function_name" { description = "Name of the lambda function" type = string default = "python_tf_lambda-ecr" } variable "ecr_image_uri" { description = "URI of the ECR image" type = string default = "381492289756.dkr.ecr.ap-northeast-1.amazonaws.com/my-python-lambda-repo:latest" } ``` > [!Important] > **Check the `region` in `providers.tf`** > [!Important] > **Edit the `variable.tf` with the correct value** </aside> ### Run the terraform ```bash terraform init ``` ```bash terraform apply # Enter yes after applying ``` <br> > [!Tip] > To checkout all the lambda functions > ```bash > aws lambda list-functions --query "Functions[].FunctionName" --region <region> > ``` # Reference {%preview https://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images-create.html %} {%preview https://docs.aws.amazon.com/zh_tw/lambda/latest/dg/python-image.html#python-image-instructions %}