Skip to content

Instantly share code, notes, and snippets.

@okelet
Last active July 7, 2025 06:24
Show Gist options
  • Select an option

  • Save okelet/c5555c85d291046996f442fcd67cdbe1 to your computer and use it in GitHub Desktop.

Select an option

Save okelet/c5555c85d291046996f442fcd67cdbe1 to your computer and use it in GitHub Desktop.

Lambda Package Builder for AWS

A shell script that builds deployment packages for AWS Lambda functions using uv package manager. Supports both x86_64 and arm64 architectures.

Usage

From Terraform:

variable "lambda_architecture" {
  type    = string
  default = "arm64"
}

variable "lambda_python_version" {
  type    = string
  default = "3.12"
}

# https://registry.terraform.io/providers/-/null/latest/docs/resources/resource
# Only triggered when there is any change in pyproject.toml, uv.lock or src directory
resource "null_resource" "prepare_lambda_files" {
  triggers = {
    policy_sha1 = sha1(join(
      "-",
      [
        sha1(file("build_lambda_package.sh")),
        sha1(file("pyproject.toml")),
        sha1(file("uv.lock")),
        join("", [for f in fileset("src", "**") : filesha1("src/${f}")]),
      ]
    ))
  }
  provisioner "local-exec" {
    command = <<EOT
      bash ${path.module}/build_lambda_package.sh . src ${path.module} ${var.lambda_architecture} ${var.lambda_python_version}
    EOT
  }
}

# https://registry.terraform.io/providers/-/archive/latest/docs/data-sources/file
data "archive_file" "lambda_zip" {
  depends_on  = [null_resource.prepare_lambda_files]
  type        = "zip"
  source_dir  = "${path.module}/.terraform/lambda_build"
  output_path = "${path.module}/.terraform/lambda.zip"
}

# https://registry.terraform.io/providers/-/aws/latest/docs/resources/lambda_function
# nosemgrep: aws-lambda-x-ray-tracing-not-active
resource "aws_lambda_function" "app" {
  function_name    = "demo"
  handler          = "app.handler"
  runtime          = "python${var.lambda_python_version}"
  role             = aws_iam_role.lambda_exec.arn
  memory_size      = 128
  timeout          = 5
  architectures    = [var.lambda_architecture]
  filename         = data.archive_file.lambda_zip.output_path
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256
}

Direct:

./build_lambda_package.sh <project_path> <code_path> <module_path> <lambda_arch> <python_version>

Parameters

  • project_path: Path to the project root containing dependencies
  • code_path: Path to Lambda function code
  • module_path: Path to Terraform module directory
  • lambda_arch: Target architecture (x86_64 or arm64)
  • python_version: Python version to build for

Requirements

  • uv package manager
  • Bash shell
  • Unix-like environment

Features

  • Builds platform-specific Lambda deployment packages
  • Handles dependency management with uv
  • Creates deployment-ready ZIP package
  • Supports multiple Python versions
  • Displays final package size

The script is designed to work with Terraform deployments and uses uv for modern Python dependency management.

#!/bin/bash
# Adapted from https://docs.astral.sh/uv/guides/integration/aws-lambda/#deploying-a-zip-archive
set -ex
usage() {
echo "Usage: $0 <project_path> <code_path> <module_path> <lambda_arch> <python_version> [<output_file_dir>]" >&2
}
PROJECT_PATH="${1}"
if [ -z "${PROJECT_PATH}" ]; then
usage
exit 1
fi
PROJECT_PATH=$(readlink -f "${PROJECT_PATH}")
CODE_PATH="${2}"
if [ -z "${CODE_PATH}" ]; then
usage
exit 1
fi
CODE_PATH=$(readlink -f "${CODE_PATH}")
MODULE_PATH="${3}"
if [ -z "${MODULE_PATH}" ]; then
usage
exit 1
fi
MODULE_PATH=$(readlink -f "${MODULE_PATH}")
LAMBDA_ARCH="${4}"
if [ -z "${LAMBDA_ARCH}" ]; then
usage
exit 1
fi
PY_VERSION="${5}"
if [ -z "${PY_VERSION}" ]; then
usage
exit 1
fi
OUTPUT_FILE_DIR="${6}"
if [ -z "${OUTPUT_FILE_DIR}" ]; then
OUTPUT_FILE_DIR="lambda_build"
fi
if [ "${LAMBDA_ARCH}" == "x86_64" ]; then
PYTHON_PLATFORM="x86_64-manylinux2014"
elif [ "${LAMBDA_ARCH}" == "arm64" ]; then
PYTHON_PLATFORM="aarch64-manylinux2014"
else
echo "Unsupported architecture: ${LAMBDA_ARCH}" >&2
exit 1
fi
# Cleanup
rm -Rf "${MODULE_PATH}/.terraform/lambda/${OUTPUT_FILE_DIR}"
# Create directory
mkdir -p "${MODULE_PATH}/.terraform/lambda/${OUTPUT_FILE_DIR}"
# Sync packages, if needed
cd "${PROJECT_PATH}" || exit 1
uv sync -q
uv export -q --frozen --no-dev --no-editable -o "${MODULE_PATH}/.terraform/lambda/${OUTPUT_FILE_DIR}/requirements.txt"
# Install requirements
# https://docs.astral.sh/uv/guides/integration/aws-lambda/#deploying-a-zip-archive
uv pip install \
--no-installer-metadata \
--no-compile-bytecode \
--python-platform "${PYTHON_PLATFORM}" \
--python-version "${PY_VERSION}" \
--target "${MODULE_PATH}/.terraform/lambda/${OUTPUT_FILE_DIR}" \
-r "${MODULE_PATH}/.terraform/lambda/${OUTPUT_FILE_DIR}/requirements.txt"
# Change to code path and add local code
cd "${CODE_PATH}" || exit 1
cp -av ./* "${MODULE_PATH}/.terraform/lambda/${OUTPUT_FILE_DIR}"
# Display package size
echo "Package size: $(du -sh "${MODULE_PATH}/.terraform/lambda/${OUTPUT_FILE_DIR}")"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment