Upcoming IaCConf: Building at the Intersection of AI and IaC 🤖

Register Now ➡️

How to Use AWS Lambda Layers With Terraform

terraform

A Lambda layer is a packaging mechanism that lets you store and share code or dependencies separately from your Lambda functions. Instead of putting every library, utility module, or binary inside each function ZIP, you publish them once as a layer and then attach that layer to one or more functions.

In this article, you will learn how to use AWS Lambda layers with Terraform, why layers help, and how to version and reuse them across multiple functions.

What is an AWS Lambda layer?

A Lambda layer is a versioned ZIP file that contains shared code, libraries, binaries, or extensions that Lambda mounts at runtime under /opt. You can attach up to five layers to a function. Once a version is published, it is immutable and can be reused across many functions, which helps keep your function packages small and consistent.

Common use cases for Lambda layers in Terraform include:

  • Standardizing shared dependencies across many functions by packaging common libraries once and attaching them via a versioned, Terraform-managed layer.
  • Reducing function package size and deployment time by moving heavy dependencies into a reusable layer.
  • Enforcing consistent internal tooling or utilities such as logging, metrics, and auth helpers without duplicating code in every function.
  • Managing safe, version-controlled upgrades by publishing new layer versions in Terraform and pinning functions to specific ARNs.

What does the Lambda layer do?

A Lambda layer acts like an additional filesystem that is mounted into the runtime environment of your function. When the function starts, AWS adds the layer’s contents to the runtime’s library path.

  • For Python, everything inside the layer’s python/ folder is added to sys.path.
  • For Node.js, dependencies go under nodejs/node_modules or a version specific path such as nodejs/node20/node_modules, which Lambda adds to NODE_PATH.
  • For other runtimes, the conventions vary, but the idea is the same.

This means your function can import code from the layer as if it were packaged inside the function ZIP.

In Terraform, a layer is modeled as an immutable, versioned resource using aws_lambda_layer_version. Each publish creates a new version ARN. Functions reference that version’s ARN in the layers field. This keeps function packages slim, standardizes dependencies, and makes rollouts or rollbacks predictable by switching which layer version a function targets.

Example 1: Create a basic Lambda layer and use it in one function

In this first example, let’s create a simple Python Lambda layer that contains shared dependencies, then attach that layer to a single Lambda function using Terraform. 

Assume you already have a ZIP file that contains your layer content in a folder structure compatible with Lambda, for example python/ for Python modules. You can build this archive with a simple packaging script.

In Terraform, the process has two main steps. First, you define the Lambda layer using the aws_lambda_layer_version resource. Second, you define the Lambda function and attach the layer by passing the layer ARN in the layers argument.

Here is a complete Terraform snippet for this scenario:

provider "aws" {
  region = "us-east-1"
}

resource "aws_lambda_layer_version" "common_libs" {
  filename   = "${path.module}/layers/common_libs.zip"
  layer_name = "common-libs"

  compatible_runtimes = [
    "python3.12",
    "python3.11",
  ]

  description = "Shared Python dependencies for Lambda functions"

  source_code_hash = filebase64sha256("${path.module}/layers/common_libs.zip")
}

resource "aws_iam_role" "lambda_exec_role" {
  name = "lambda-exec-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        Service = "lambda.amazonaws.com"
      }
      Action = "sts:AssumeRole"
    }]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
  role       = aws_iam_role.lambda_exec_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_lambda_function" "hello_with_layer" {
  function_name = "hello-with-layer"
  runtime       = "python3.11"
  handler       = "handler.lambda_handler"
  role          = aws_iam_role.lambda_exec_role.arn

  filename         = "${path.module}/functions/hello.zip"
  source_code_hash = filebase64sha256("${path.module}/functions/hello.zip")

  layers = [
    aws_lambda_layer_version.common_libs.arn
  ]

  environment {
    variables = {
      STAGE = "dev"
    }
  }
}

In this configuration, the Lambda layer is fully managed by Terraform. Because the layer uses source_code_hash, when you update common_libs.zip and run terraform apply, Terraform detects the change and creates a new layer version.

The function references the latest version through the resource dependency, so it receives an updated layer ARN. Your code can then import any library bundled in that layer as if it were part of local site-packages. 

This pattern keeps shared runtime dependencies separate from application-specific code.

Example 2: Share one Lambda layer across multiple functions

The second example shows how to reuse a single Terraform Lambda layer across several Lambda functions in the same module. 

The basic idea is that you define the layer only once using aws_lambda_layer_version. Then you reference its ARN from each aws_lambda_function through the layers argument. Terraform handles dependencies automatically. All functions point to the same layer version, which avoids duplication and keeps your infrastructure definition tidy.

Here is a complete Terraform example using two functions that share the same layer.

provider "aws" {
  region = "us-east-1"
}

resource "aws_lambda_layer_version" "utilities" {
  filename   = "${path.module}/layers/utilities.zip"
  layer_name = "shared-utilities"

  compatible_runtimes = [
    "nodejs20.x",
    "nodejs18.x",
  ]

  description = "Shared Node.js utilities for multiple Lambda functions"

  source_code_hash = filebase64sha256("${path.module}/layers/utilities.zip")
}

resource "aws_iam_role" "lambda_role" {
  name = "lambda-shared-layer-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        Service = "lambda.amazonaws.com"
      }
      Action = "sts:AssumeRole"
    }]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
  role       = aws_iam_role.lambda_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_lambda_function" "create_order" {
  function_name = "create-order"
  runtime       = "nodejs20.x"
  handler       = "index.handler"
  role          = aws_iam_role.lambda_role.arn

  filename         = "${path.module}/functions/create-order.zip"
  source_code_hash = filebase64sha256("${path.module}/functions/create-order.zip")

  layers = [
    aws_lambda_layer_version.utilities.arn
  ]

  environment {
    variables = {
      SERVICE_NAME = "orders"
    }
  }
}

resource "aws_lambda_function" "get_order" {
  function_name = "get-order"
  runtime       = "nodejs20.x"
  handler       = "index.handler"
  role          = aws_iam_role.lambda_role.arn

  filename         = "${path.module}/functions/get-order.zip"
  source_code_hash = filebase64sha256("${path.module}/functions/get-order.zip")

  layers = [
    aws_lambda_layer_version.utilities.arn
  ]

  environment {
    variables = {
      SERVICE_NAME = "orders"
    }
  }
}

Both create_order and get_order rely on the shared-utilities layer. Inside your Node.js code, you can require modules included in the layer like any other dependency. 

To roll out an update to the shared utilities, rebuild utilities.zip and run Terraform. Terraform sees the new source_code_hash, creates a new layer version and updates both functions to use that version, which reduces operational overhead and keeps all functions in sync.

Key points

Using Lambda layers with Terraform provides a clean, efficient way to manage shared code, dependencies, and runtime extensions across many AWS Lambda functions. By defining layers as versioned infrastructure, you can centralize common libraries, reduce deployment package sizes, and keep functions consistent and easier to maintain.

Terraform is really powerful, but to achieve an end-to-end secure GitOps approach, you need to use a product that can run your Terraform workflows. Spacelift takes managing Terraform to the next level by giving you access to a powerful CI/CD workflow and unlocking features such as:

  • Policies (based on Open Policy Agent)
  • Multi-IaC workflows
  • Self-service infrastructure
  • Integrations with any third-party tools

If you want to learn more about Spacelift, create a free account today or book a demo with one of our engineers.

Note: New versions of Terraform are placed under the BUSL license, but everything created before version 1.5.x stays open-source. OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.

Terraform management made easy

Spacelift effectively manages Terraform state, more complex workflows, supports policy as code, programmatic configuration, context sharing, drift detection, resource visualization, and includes many more features.

Learn more

Frequently asked questions

  • How many Lambda layers can I attach to a single function?

    You can attach up to five Lambda layers per function.

  • How do I update a Lambda layer without breaking functions?

    To update a Lambda layer, publish a new aws_lambda_layer_version and keep previous versions available. Pin each function to a specific layer version ARN and update functions on your schedule.

Terraform Commands Cheat Sheet

Grab our ultimate cheat sheet PDF
for all the Terraform commands
and concepts you need.

Share your data and download the cheat sheet