An Introduction to AWS Lambda Layers

Using Terraform to build and deploy shared code as layers.

AWS Lambda Layers are a feature designed to simplify dependency management and promote code reuse in serverless applications. For this example, I'll use the npm module @aws-sdk/client-s3vectors since at the time of writing, it is not included in the Lambda runtime scope.

We begin by creating a directory for the layer. The layer's code must be under a nodejs directory.

.
└── aws-sdk-s3vectors-layer
    └── nodejs
        ├── package-lock.json
        └── package.json

We then install our dependencies into the layer module.

{
  "name": "aws-sdk-client-s3vectors",
  "dependencies": {
    "@aws-sdk/client-s3vectors": "^3.862.0"
  }
}

The layer must be built before deploying. We can use Terraform to run npm install in the nodejs directory before compressing it into a zip archive.

variable "project_name" {
  type = string
}

variable "service_name" {
  type = string
  default = "layers"
}

resource "null_resource" "install_node_modules" {
  provisioner "local-exec" {
    working_dir = "${path.module}/src/aws-sdk-s3vectors-layer/nodejs"
    command     = "npm install"
  }
  triggers = {
    always_run = "${timestamp()}"
  }
}

data "null_data_source" "wait_for_install_node_modules" {
  inputs = {
    lambda_exporter_id = "${null_resource.install_node_modules.id}"
  }
}

data "archive_file" "s3vectors_zip" {
  depends_on = [ data.null_data_source.wait_for_install_node_modules ]
  type             = "zip"
  source_dir       = "${path.module}/src/aws-sdk-s3vectors-layer"
  output_file_mode = "0666"
  output_path      = "${path.module}/src/aws-sdk-s3vectors-layer.zip"
}

Now, we're ready to provision the layer.

resource "aws_lambda_layer_version" "s3vectors_layer" {
  layer_name          = "${var.project_name}-${var.service_name}-s3vectors-sdk-${terraform.workspace}"
  filename            = data.archive_file.s3vectors_zip.output_path
  source_code_hash    = data.archive_file.s3vectors_zip.output_base64sha256
  compatible_runtimes = ["nodejs20.x"]
  description         = "Lambda Layer for @aws-sdk/client-s3vectors"
}

Finally, we reference the layer's ARN in the Lambda function's configuration.

resource "aws_lambda_function" "example" {
  filename      = "function.zip"
  function_name = "example_layered_function"
  role          = aws_iam_role.example.arn
  handler       = "index.handler"
  runtime       = "nodejs20.x"

  layers = [aws_lambda_layer_version.s3vectors_layer.arn]
}