---
# System prepended metadata

title: Deploy Lambda with Docker Image and Terraform
tags: [AWS, ECR, Terraform, Lambda]

---

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