NAME

Amazon::Lambda::Runtime::Builder - scaffolding, environment checker, and deployment toolchain for Perl Lambda container images

SYNOPSIS

# Scaffold a new project in the current directory
alr-builder install

# Scaffold into a specific directory
alr-builder install --install-dir /path/to/my-lambda

# Verify tools and IAM permissions before your first build
alr-builder check

# Validate lambda.env (or lambda.yaml) against this Lambda's requirements
alr-builder check-env-file

# Migrate an existing lambda.env to lambda.yaml
alr-builder generate-yaml

DESCRIPTION

Amazon::Lambda::Runtime::Builder is the companion deployment tool for Amazon::Lambda::Runtime. It handles everything outside of the Perl runtime itself: scaffolding a new project directory, verifying your build environment, and providing the make-based build/deploy pipeline that turns a handler distribution into a deployed Lambda function wired to the event source of your choice.

Five trigger types are supported - s3-sqs, s3-direct, eventbridge, sns, and alb - selected with a single TRIGGER_TYPE value and provisioned by one dispatching target (make lambda-pipeline). See "CONFIGURATION" and "WORKFLOW".

The alr-builder CLI provides four commands:

A second, internal CLI - alr-helper - wraps the AWS API calls the Makefiles invoke (ECR, IAM, Lambda, SQS, SNS, S3, EventBridge, ELBv2, CloudWatch Logs, STS). You do not normally call it directly; the Makefiles do.

COMMANDS

install

alr-builder install [--install-dir DIR] [--force]

Copies the project scaffold into the target directory (defaults to the current working directory), creating the directory if it does not exist. The set of installed files is driven by the distribution's share/MANIFEST.json:

The Dockerfile, the trigger Makefiles (sqs.mk, sns.mk, s3.mk, eventbridge.mk, alb.mk, streaming.mk), and the platform/overlay/ log-group fragments are not copied into the project - they are read from this distribution's share directory at build time. The cpanfile used to build the image is generated on demand from your distribution tarball's META.json (see "WORKFLOW").

Use --force to overwrite a file whose manifest policy would otherwise refuse to replace it.

check

alr-builder check

Verifies that your environment is ready to build and deploy. Checks two things:

Required system tools - docker and make must be on your PATH. curl is checked as an optional tool. The tool list comes from MANIFEST.json. Missing required tools are reported as errors.

IAM permissions - if Amazon::API::IAM, Amazon::API::STS, and Amazon::Credentials are installed, calls SimulatePrincipalPolicy using your current credentials to confirm your IAM identity has the permissions required to build and deploy. See "REQUIRED IAM PERMISSIONS" for the full list.

If the IAM modules are not installed, tool checks still run but permission checking is skipped with a warning.

check-env-file

alr-builder check-env-file

Validates the project's configuration against this Lambda's requirements (see "CONFIGURATION"). Reads lambda.env if present - an absent lambda.env is treated as "nothing configured yet", useful for seeing the full set of required and defaulted values for a brand-new project - and reports three groups:

Fields are scoped to the active trigger.type via lambda-mapping.yml's applies_to, so only the values relevant to your trigger are reported. Exits non-zero if any required values are missing.

generate-yaml

alr-builder generate-yaml

Migrates an existing lambda.env to lambda.yaml. Requires lambda.env to exist and lambda.yaml to not already exist - this is a one-time migration step, not something to re-run once lambda.yaml is your source of truth (see "CONFIGURATION").

Performs the same validation as check-env-file first; if any required values are missing, no lambda.yaml is written and the missing fields are reported instead. Otherwise, writes a minimal lambda.yaml containing only values that differ from their defaults (plus any field with no default) - fields matching their default are omitted, since Makefile.mk applies the same defaults via lambda-mapping.yml.

OPTIONS

CONFIGURATION

Your Lambda's configuration - function name, memory, timeout, trigger type, trigger details, and so on - lives in lambda.env, a flat KEY = value file that Makefile.mk reads via -include lambda.env. Every field has a corresponding entry in lambda-mapping.yml (installed as part of this distribution), which defines each field's env name, default, whether it is required, and which trigger types it applies_to.

lambda.env can be managed in either of two ways:

To move an existing lambda.env to the lambda.yaml workflow, run alr-builder generate-yaml once.

Trigger types

trigger.type (TRIGGER_TYPE) selects the event source and the pipeline that provisions it:

A Function URL with streaming responses is also available for any function (see lambda-function-url and test-streaming in "MAKEFILE TARGETS"); it is independent of TRIGGER_TYPE.

lambda.yaml structure

Fields common to every trigger type:

image:
  repo: ...            # ECR repository / image name (REPO_NAME)
  handler: ...         # Perl handler class (HANDLER_CLASS) - required
lambda:
  name: ...            # function name (FUNCTION_NAME)
  timeout: ...         # seconds (TIMEOUT)
  memory: ...          # MB (MEMORY)
  concurrency: ...     # reserved concurrency (CONCURRENCY)
role:
  name: ...            # IAM role name (ROLE_NAME)
  profile: ...         # named policy profile (ROLE_PROFILE)
log_retention: ...     # CloudWatch retention in days (LOG_RETENTION)
platform_image: ...    # optional platform layer image (PLATFORM_IMAGE)
overlay: ...           # optional overlay image name (OVERLAY)
trigger:
  type: ...            # one of s3-sqs, s3-direct, eventbridge, sns, alb - required

Trigger-specific trigger: keys, by type:

# s3-sqs
trigger:
  type: s3-sqs
  bucket: ...          # source S3 bucket (BUCKET_NAME) - required
  prefix: ...          # key prefix filter (KEY_PREFIX)
  event: ...           # S3 event type (S3_EVENT)
  queue:
    name: ...                     # (QUEUE_NAME)
    batch_size: ...               # (BATCH_SIZE)
    visibility_timeout: ...       # seconds (VISIBILITY_TIMEOUT)
    retention: ...                # seconds (RETENTION)
    receive_count: ...            # max receives before DLQ (RECEIVE_COUNT)
    partial_batch_response: ...   # true/false (PARTIAL_BATCH_RESPONSE)
    dlq:
      name: ...          # DLQ name (DLQ_NAME)
      retention: ...     # seconds (DLQ_RETENTION)

# s3-direct
trigger:
  type: s3-direct
  bucket: ...          # source S3 bucket (BUCKET_NAME) - required
  prefix: ...          # key prefix filter (KEY_PREFIX)
  event: ...           # S3 event type (S3_EVENT)

# eventbridge
trigger:
  type: eventbridge
  schedule: ...        # schedule expression (SCHEDULE_EXPRESSION)
  rule_name: ...       # rule name (RULE_NAME)

# sns
trigger:
  type: sns
  topic_name: ...      # SNS topic name (TOPIC_NAME) - required

trigger: type: alb listener_arn: ... # existing listener ARN (LISTENER_ARN) - required path: ... # path regex to route (ALB_PATH; default /build) priority: ... # listener-rule priority (RULE_PRIORITY; default 999)

priority defaults to 999 so that, on a shared ALB, this path-only rule is evaluated after any host-specific or more-specific rules already present. On a shared load balancer, prefer adding a host condition to scope the rule to your own hostname rather than relying on priority ordering alone.

Every field except the ones marked required may be omitted, in which case lambda-mapping.yml's default applies. See "MAKEFILE TARGETS" for how role.profile is applied.

config.mk

lambda.env/lambda.yaml hold per-Lambda configuration - the values that describe this function. Tool-level settings that apply across every project on a given machine or in a given shell - which AWS profile or account to use, a DarkPAN resolver, and so on - don't belong there, since lambda.env may be regenerated from lambda.yaml at any time.

config.mk is an optional make fragment, read via -include config.mk at the very top of Makefile.mk, before lambda.env is loaded, for exactly this kind of machine- or shell-local override. alr-builder install does not create one; if your project's Makefile was bootstrapped with CPAN::Maker::Bootstrapper, a default config.mk is already there. Otherwise add it yourself:

# config.mk
AWS_PROFILE = my-profile
AWS_ACCOUNT = 123456789012
RESOLVER    = 02packages,https://cpan.openbedrock.net/orepan2

Because it loads first, anything set here is a default that lambda.env and the environment can still override (Makefile.mk uses ?= for the values it reads). Keep it out of version control if it holds machine-specific or credential-adjacent values - add it to .gitignore.

WORKFLOW

The typical workflow for a new Lambda function:

  1. Scaffold the project:

     alr-builder install --install-dir my-lambda
     cd my-lambda
    
  2. Verify your environment:

     alr-builder check
    
  3. Implement your handler - edit the generated lib/_App_/_Path_/Lambda/Handler.pm and the Event/*.pm module for your trigger type (or replace either with your own), and add their dependencies to your distribution's cpanfile/META.json as you introduce them.

  4. Configure the Lambda - alr-builder install already wrote a lambda.yaml with image.handler and trigger.type set from the app-name/trigger-type you gave it. Fill in any trigger-specific required values it doesn't have a default for (e.g. trigger.bucket for s3-sqs/s3-direct, trigger.topic_name for sns, trigger.listener_arn for alb), and adjust anything else - function name, memory, timeout, schedule, and so on - as needed. Run alr-builder check-env-file to confirm nothing required is still missing (see "CONFIGURATION").
  5. Build a CPAN distribution - turn your handler into a standard CPAN distribution and produce a tarball. make image resolves DIST_TARBALL to the most recent $(DIST_NAME)-*.tar.gz in $(BUILDER_HOME), where DIST_NAME comes from that distribution's META.json.

    If you don't already have distribution tooling for the project, consider CPAN::Maker::Bootstrapper - it scaffolds a complete CPAN project from the assets install just generated:

      cpanm CPAN::Maker::Bootstrapper
      bootstrapper -I . -i . MyApp
    

    After running the bootstrapper you should have a CPAN distribution tarball in your working directory.

    From there, edit your handler and run make - your tarball is rebuilt automatically. By default, make scans your files for new dependencies, updates cpanfile, lints your code (perlcritic, perltidy if available), and builds the new tarball. Use make quick to skip linting.

    See CPAN::Maker::Bootstrapper for full details.

  6. Deploy - provision the image, role, function, log group, and the event source for your trigger type in one step:

      make lambda-pipeline
    
  7. Test:

     make invoke          # or: make test-streaming (Function URL)
    
  8. Deploy subsequent changes:

     make update-function
    
  9. Tear down when finished:

     make lambda-teardown
    

Phase 1 - Build the container image

make image requires a CPAN distribution tarball ($(DIST_NAME)-*.tar.gz) to already exist in $(BUILDER_HOME) - see Workflow step 5.

The image is built in layers:

The framework Dockerfile handles all of this. Build with:

make image

The builder stage installs your distribution and its prerequisites with cpm (resolving through RESOLVER if a DarkPAN is configured), then the final stage copies the installed tree onto ${PLATFORM_IMAGE} (default perl-lambda-base:latest). The handler is selected at build time by class name:

ARG HANDLER_CLASS="LambdaHandler"
ENV LAMBDA_MODULE=${HANDLER_CLASS}
ENTRYPOINT ["/usr/local/bin/bootstrap"]

HANDLER_CLASS is required (the build errors without it) and is passed automatically from lambda.env. ENV LAMBDA_MODULE tells bootstrap which handler class to load.

To reinstall modules that are also under active development - overriding the versions resolved from CPAN/DarkPAN - list them (one bare Module@version per line) in a requires.reinstall file in the project root. When present it is copied into the build context and reinstalled in a dedicated layer. Because that layer is otherwise cached, change CACHE_BUST (or the file's contents) to force it to re-run.

Phase 2 - Provision function and event source

make lambda-pipeline is the single entry point. It:

  1. builds the platform image first if PLATFORM_IMAGE is set and a Dockerfile.platform is present;
  2. dispatches on TRIGGER_TYPE to the matching per-type pipeline (lambda-sqs-pipeline, lambda-s3-pipeline, lambda-eventbridge-pipeline, lambda-sns-pipeline, or lambda-alb-pipeline), each of which builds and pushes the image, creates the IAM role and attaches policies, creates the function, applies memory/timeout and the log-group retention policy, and wires the trigger;
  3. builds and applies the overlay image last if OVERLAY is set.

Every step is idempotent and tracked by sentinel files under .cache/$(FUNCTION_NAME), so re-running skips work that is already done.

To deploy a code change:

make update-function

This rebuilds the image, pushes to ECR using the image digest (never :latest), and updates the function code - and, if OVERLAY is set, rebuilds and re-applies the overlay.

Phase 3 - Teardown

make lambda-teardown

Dispatches on TRIGGER_TYPE to the matching teardown, removes the overlay (if OVERLAY is set), and deletes the CloudWatch log group. Shared resources are treated conservatively: an SNS topic is not deleted by default (it may have other subscribers), and the platform ECR repository must be removed manually (see platform-teardown).

The policies files

Two mechanisms control the Lambda execution role's permissions:

Note: Receiving an event from a service does not automatically grant your handler permission to call that service's APIs. An S3 trigger allows Lambda to invoke your function - it does not allow your function to read or write S3 objects. Add the appropriate managed policy ARN to policies, or scope a specific resource in custom-policies.json, then re-run make update-policies.

MAKEFILE VARIABLES

For the per-trigger configuration values (queue settings, schedule, topic, listener, and so on) see "CONFIGURATION" and lambda-mapping.yml. The variables below are tool-level or build-level and apply across trigger types.

Identity and image

Build

Function configuration and logging

Policies

Function URL / streaming

MAKEFILE TARGETS

Primary Targets

Per-trigger Pipelines

Each provisions the full stack for its trigger type and has a matching teardown. lambda-pipeline dispatches to the right one based on TRIGGER_TYPE; run one directly only if you want to bypass the dispatch.

lambda-sqs-pipeline          / lambda-sqs-teardown
lambda-s3-pipeline           / lambda-s3-teardown
lambda-eventbridge-pipeline  / lambda-eventbridge-teardown
lambda-sns-pipeline          / lambda-sns-teardown
lambda-alb-pipeline          / lambda-alb-teardown

Event Trigger and Function URL Targets

Image Layer Targets

Internal Targets

Called automatically as dependencies - you should not need to invoke these directly:

image - builds the Docker image. tarball-validated - verifies the tarball contains HANDLER_CLASS. ecr-repo - creates the ECR repository (with lifecycle policy) if absent. deploy - logs in to ECR and pushes the image. image-digest - resolves the pushed image's digest. lambda-role - creates the IAM execution role if absent. lambda-managed-policies / lambda-inline-policies - attach managed and inline policies. lambda-concurrency - sets reserved concurrency via PutFunctionConcurrency. lambda-sqs-response-types - sets the SQS event source mapping's FunctionResponseTypes from PARTIAL_BATCH_RESPONSE. policy-document - generates the IAM assume-role trust policy JSON. The image build also derives a cpanfile and Debian package list from the distribution tarball on the fly.

REQUIRED IAM PERMISSIONS

The check command verifies the following permissions via SimulatePrincipalPolicy. They cover the core build-and-deploy path for all trigger types.

ECR

ecr:CreateRepository         ecr:DescribeRepositories
ecr:GetAuthorizationToken    ecr:BatchCheckLayerAvailability
ecr:PutImage                 ecr:InitiateLayerUpload
ecr:UploadLayerPart          ecr:CompleteLayerUpload
ecr:PutLifecyclePolicy       ecr:GetLifecyclePolicy

IAM

iam:GetRole                  iam:CreateRole
iam:AttachRolePolicy         iam:PassRole
iam:ListAttachedRolePolicies

Note: iam:PassRole is frequently overlooked. Its absence produces a confusing InvalidParameterValueException stating the role cannot be assumed by Lambda even though the role exists and appears correct.

Lambda

lambda:GetFunction              lambda:CreateFunction
lambda:UpdateFunctionCode       lambda:UpdateFunctionConfiguration
lambda:GetFunctionConfiguration lambda:InvokeFunction
lambda:CreateEventSourceMapping lambda:ListEventSourceMappings
lambda:GetPolicy                lambda:AddPermission
lambda:RemovePermission         lambda:CreateFunctionUrlConfig
lambda:GetFunctionUrlConfig     lambda:DeleteFunctionUrlConfig

SQS / SNS / S3 / EventBridge / STS

sqs:ListQueues                  sqs:CreateQueue
sns:ListTopics                  sns:CreateTopic
sns:Subscribe                   sns:GetTopicAttributes
s3:CreateBucket                 s3:ListBuckets
s3:PutBucketNotificationConfiguration
events:DescribeRule             events:PutRule
events:PutTargets               events:RemoveTargets
events:DeleteRule               events:EnableRule
events:DisableRule
sts:GetCallerIdentity

Additional permissions (not verified by check)

Some features and teardown paths call APIs that check does not yet simulate. You will need these in addition to the list above when the corresponding feature is used:

Handler Runtime Permissions

AWSLambdaBasicExecutionRole covers CloudWatch logging only. Any AWS APIs your handler calls directly need additional policies. install writes a policies file pre-populated for your trigger type; for anything a managed policy doesn't cover, or where you want tighter, resource-scoped permissions, add a hand-written IAM policy document as custom-policies.json and it's applied as an inline role policy alongside the managed ones.

SEE ALSO

Amazon::Lambda::Runtime - the runtime library your handler inherits from

Amazon::Credentials - credential provider used for IAM permission checking

Amazon::API::IAM, Amazon::API::STS - AWS API clients used by check

AUTHOR

Rob Lauer - rlauer@treasurersbriefcase.com

LICENSE

(c) Copyright 2019-2026 Robert C. Lauer. All rights reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.