# 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 %}