terraform-aws-lambda: a module that builds, packages and deploys Lambda code
Terraform module, which takes care of a lot of AWS Lambda/serverless tasks (build dependencies, packages, updates, deployments) in countless combinations 🇺🇦
At a glance
- What is it?
- The terraform-aws-lambda module from terraform-aws-modules handles the whole path from source code to a running AWS Lambda function: building dependencies, assembling a deployment package, storing it locally or in S3, and creating the function, layer, alias or versioned deployment. It is powerful, it is opinionated, and the build step brings its own toolchain with it.
- Who is it for?
- terraform-aws-lambda fits a team that wants one Terraform module to own the code pipeline as well as the infrastructure, and that accepts a build step on the machine running Terraform, whether that is a developer laptop or a CI runner. It does not fit a team whose artefacts are built and signed by a separate pipeline, or one that wants Lambda resources and nothing else.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 15 days ago.
- What is it written in?
- Mainly HCL, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the module covers and why it exists
terraform-aws-lambda is a Terraform module for AWS Lambda, and the README describes it as the part of the serverless.tf framework that aims to simplify all operations when working with the serverless in Terraform. The framework belongs to the same maintainer, antonbabenko, and the module is the piece most teams adopt on its own.
The module's scope is wider than creating a Lambda resource. The README lists six things it does: build and install dependencies for a function or a layer, which requires Python 3.6 or newer; create, store and use deployment packages; create, update and publish functions and layers; create static and dynamic aliases; do complex deployments such as rolling, canary, rollbacks and triggers; and use the AWS SAM CLI to test a function.
The build step is the reason the module exists in this form, and it is also what distinguishes it from a plain resource wrapper. Dependencies are built locally or in Docker, with or without SSH agent support for private builds, for any runtime and architecture that AWS Lambda supports. Packages can be created or an existing one deployed, from local disk, from S3, from a URL or from an ECR repository. Packages can be stored locally or in an S3 bucket.
A team that has already solved packaging with a separate pipeline may find that half of this redundant. A team that has not will find it is the part that would otherwise be a pile of shell scripts in CI, and the module is written by someone who clearly had to write those scripts first.
A first function: the smallest useful module block
The README's first example is the one to start from, because it is the module doing only the obvious things: taking a directory of source, building it, and creating a function from it.
module "lambda_function" {
source = "terraform-aws-modules/lambda/aws"
function_name = "my-lambda1"
description = "My awesome lambda function"
handler = "index.lambda_handler"
runtime = "python3.12"
source_path = "../src/lambda-function1"
tags = {
Name = "my-lambda1"
}
}The interesting inputs are source_path, which is the directory the build runs in, and the runtime, which is python3.12 here. The module is a registry module, so source points at terraform-aws-modules/lambda/aws and the version is pinned in the usual way in your own configuration.
What you should see after a plan and apply is a deployment package built from that directory and an aws_cloudwatch_function resource created from it. The first apply is the slow one, because the dependency build runs then, and that is the step most likely to need Python and a working toolchain for your language on the machine running Terraform.
The README's example set in the repository covers the variations, with directories for alias, async, build-package, code-signing, complete, container-image, deploy, event-source-mapping, multiple-regions, runtimes, simple-cicd, simple, triggers, with-efs, with-vpc-s3-endpoint and with-vpc. That directory list is a reasonable map of what people actually configure.
Layers, S3 storage, and the bucket the module will not create for you
A layer is created by setting create_layer in the same module, and the layer gets its own block of attributes:
module "lambda_layer_local" {
source = "terraform-aws-modules/lambda/aws"
create_layer = true
layer_name = "lambda-layer-s3"
description = "My amazing lambda layer (deployed from S3)"
compatible_runtimes = ["python3.12"]
source_path = "../src/lambda-layer"
store_on_s3 = true
s3_bucket = "my-bucket-id-with-lambda-builds"
}The same module instance can produce both a function and a layer, and the README shows a function that references the layer's output through layers = [module.lambda_layer_s3.lambda_layer_arn].
store_on_s3 and s3_bucket move the artefact out of local state. The README lists storage locations as local or S3, and package sources as local, S3, URL or ECR. Setting store_on_s3 makes the module upload what it built, and it expects the bucket to exist, since s3_bucket is given as an id in the examples rather than created by the module.
That limit is stated in the README in one line and it catches people out: the module does not copy prebuilt packages into the S3 bucket, and it can only store packages it builds itself and stores in the bucket. If your artefact was built elsewhere and you want it in S3, the module will not do the copy for you.
Bringing your own artefact: local files, S3 objects and container images
The module can also skip the build entirely and deploy something that already exists, which is what a team with an external build pipeline needs.
For a package that is already on disk, create_package is set to false and local_existing_package points at the zip. For a package that is already in S3, create_package is false again and s3_existing_package takes a bucket and key, which the README's example wires to an aws_s3_object resource it manages itself. The key in that example is derived from the file's MD5, so the object name changes when the artefact changes, which is the usual trick for forcing a new version on a function.
The container image path is the third option, and it is the simplest of the three because there is no artefact to move:
module "lambda_function_container_image" {
source = "terraform-aws-modules/lambda/aws"
function_name = "my-lambda-existing-package-local"
description = "My awesome lambda function"
create_package = false
image_uri = "132367819851.dkr.ecr.eu-west-1.amazonaws.com/complete-cow:1.0"
package_type = "Image"
}Setting create_package to false and package_type to Image tells the module to point the function at an image you have already pushed to ECR. Note that the README warns, in the context of the S3 example, that changes in the local_existing_package value may trigger deployment via Terraform, which is the behaviour to keep in mind for any externally produced artefact.
Separating code from infrastructure with ignore_source_code_hash
One of the module's more useful options exists for teams where the function code and the infrastructure live in different places, such as different repositories, teams or CI/CD pipelines.
The README's approach is to turn off source code tracking, which turns off deployments and rollbacks performed by the module, and to deploy a dummy function. The setting is ignore_source_code_hash = true:
module "lambda_function_externally_managed_package" {
source = "terraform-aws-modules/lambda/aws"
function_name = "my-lambda-externally-managed-package"
description = "My lambda function code is deployed separately"
handler = "index.lambda_handler"
runtime = "python3.12"
create_package = false
local_existing_package = "./lambda_functions/code.zip"
ignore_source_code_hash = true
}With that in place, the infrastructure and IAM permissions, policies and events are managed by Terraform, while the code is updated by an external tool, with the AWS CLI's update-function-code given as the example. The module then stops reacting to code changes, which is exactly what you want when another pipeline owns the artefact.
The two caveats are in the README and both are worth reading twice. The module must not be told to deploy a dummy function that will be replaced, because the code update happens outside Terraform, and a change in the local_existing_package value may still trigger a deployment through Terraform, which is the one path that bypasses the separation you just configured.
Aliases, deployments and the rest of the Lambda surface
The module does not stop at the function. The README lists support for almost all features of Lambda resources, including functions, layers and aliases, and for nearly all aspects of them: provisioned concurrency, VPC, EFS, dead-letter notification, tracing, async events, event source mapping, IAM role and IAM policies, among others. It also supports Lambda@Edge and conditional creation for many types of resources.
Two behaviours are worth singling out. The first is that execution of nearly any step in the process can be controlled, that is build, package, store package, deploy and update. In practice that means a pipeline can build without deploying, or deploy without rebuilding, which is what makes a CI setup sane rather than a loop that rebuilds on every plan.
The second is the module split. Aliases live in modules/alias, and the README describes them as static and dynamic aliases, with static and dynamic pointing at different versions in the standard way. Complex deployments live in modules/deploy, covering rolling deployments, canary releases, rollbacks and triggers. The examples directory in the repository has matching entries for alias and deploy, so each is usable on its own rather than only through the parent module.
There is also a SAM CLI integration, so a function defined here can be tested with the AWS SAM CLI before it goes anywhere near your account, and a code-signing example directory, which matters for anyone whose artefacts are signed.
For the rest of the machinery, the repository layout gives the shape: main.tf, package.tf, iam.tf, variables.tf, outputs.tf, versions.tf, a package.py at the root, modules/, wrappers/, examples/, tests/, tox.ini, a .pre-commit-config.yaml and a .releaserc.json.
Where this module is the wrong tool
The first limit is the build environment. The dependency build requires Python 3.6 or newer on the machine running Terraform, and it runs for whatever language your function uses, locally or in Docker. That means a Terraform run is no longer a plan and apply, it is a build, which changes what CI looks like, what a developer's laptop needs, and what happens when the build fails. Teams that already build and sign artefacts in their own pipeline are the ones who find this redundant, and the ignore_source_code_hash option exists precisely for them.
The second limit is that the module is not complete. The README says almost all features and nearly all aspects, and the wording is doing real work there. Lambda keeps adding knobs, and a module that tracks a moving target will always be a release or two behind on the newest ones. The version history shows why the phrasing is careful: v8.12.0, v8.11.0 and v8.10.0 were all published on 2026-10-07, which is a fast cadence for a module, and it means the module moves when AWS moves.
The third limit is where the state lives. Packages stored locally live on the machine that ran the build, which is a problem for a state file shared between runners, and packages stored in S3 need a bucket you manage. The module writes the artefact and its hash into Terraform state, which is convenient for change detection and uncomfortable if you treat state as sensitive, because a package hash in state is a fingerprint of your code.
The fourth limit is scope. If you deploy container images and never build a zip, this module is more than you need, since the ECR path turns the module into a resource wrapper. And if your organisation standardises on a different IaC tool, the whole ecosystem of examples, alias modules and deploy modules does not travel.
The alternative is to use the AWS provider resources directly, aws_cloudwatch_function and aws_lambda_layer, and keep packaging in CI. The difference in approach is where the boundary between code and infrastructure sits: this module deliberately owns both, which is convenient when one team owns both and awkward when two teams with two pipelines meet at the artefact.
Maintenance, versioning and the licence
The project is not archived, the last push was on 2026-10-08, and the three most recent releases are v8.10.0, v8.11.0 and v8.12.0, all published on 2026-10-07. Publishing three versions in a few minutes is what a release pipeline does when the tags are cut in sequence, and the .releaserc.json in the repository root indicates that releases are automated from a semantic-release configuration rather than hand-prepared.
That automation is what makes the versioning predictable. The major version moves when the interface of the module changes in a way that requires action from users, and the minor versions in the 8.x line are additive. For a module with a large public surface, that is the arrangement you want, because a surprise major version in Terraform is a plan that fails in CI.
The repository also shows the testing discipline. There is a tests/ directory, a tox.ini for the Python tooling, and a .pre-commit-config.yaml, alongside a CHANGELOG.md that is the place to look before a version bump. The examples/ directory doubles as documentation that is compiled rather than written, which is why the directory list in the README is long: every feature combination tends to get an example.
The licence is Apache-2.0, with a LICENSE file at the root. For a Terraform module, the licence matters less than the interface, but Apache-2.0 includes an express patent grant and no copyleft obligation, so a derived module of your own is straightforward.
One operational habit follows from all this. Because the module is a registry dependency that updates quickly, pin the version in your own configuration, read the CHANGELOG.md before bumping it, and run a plan on a non-production workspace first, since a change in the module's own behaviour can rewrite the deployment hash of a function even when your configuration did not change.
Editorial conclusion
terraform-aws-lambda fits a team that wants one Terraform module to own the code pipeline as well as the infrastructure, and that accepts a build step on the machine running Terraform, whether that is a developer laptop or a CI runner. It does not fit a team whose artefacts are built and signed by a separate pipeline, or one that wants Lambda resources and nothing else. Verify first where your artefact comes from, because the module only stores packages it builds itself, set ignore_source_code_hash = true if the code is managed elsewhere, and read the outputs of the alias and deploy submodules before you rely on a rolling or canary release in production.
Frequently asked questions
How do I create a Lambda function with the terraform-aws-lambda module?
Add a module block with source = "terraform-aws-modules/lambda/aws", set function_name, handler, runtime and source_path, and let the module build the deployment package and create the function. The README's first example uses runtime = "python3.12" and source_path pointing at the directory with your function code.
Can the module deploy a package I built myself?
Yes. Set create_package = false and either local_existing_package for a zip on disk or s3_existing_package for an object already in S3. The README also notes that this module does not copy prebuilt packages into the S3 bucket, because it can only store packages it builds itself.
How do I keep Lambda code updates out of Terraform when another pipeline owns the code?
Set ignore_source_code_hash = true, which turns off deployments and rollbacks performed by the module, and deploy a dummy function whose code is then updated with an external tool such as the AWS CLI's update-function-code. The README warns that changes in the local_existing_package value may still trigger a deployment through Terraform.
What does the terraform-aws-lambda module need to build a function?
The README states that building and installing dependencies requires Python 3.6 or newer, and that builds run locally or in Docker, with or without SSH agent support for private builds, for any runtime and architecture supported by AWS Lambda.
What licence is the terraform-aws-lambda module under and how is it versioned?
The module is licensed under Apache-2.0, with a LICENSE file in the repository, and is published to the Terraform registry as terraform-aws-modules/lambda/aws. Releases v8.10.0, v8.11.0 and v8.12.0 were all published on 2026-10-07, and the last push was on 2026-10-08.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/terraform-aws-modules-terraform-aws-lambda)Was this useful?