Skip to content

cfn-handler

CI PyPI Python Versions Lambda Layer License

A modern, well-engineered Python library for writing AWS CloudFormation Custom Resource lifecycle handlers. Inspired by — but not derived from — the unmaintained aws-cloudformation/custom-resource-helper.

Why cfn-handler?

CloudFormation Custom Resources are AWS Lambda functions invoked during stack operations. Getting them right is harder than it looks: you must respond to a presigned URL within the Lambda timeout, handle CREATE/UPDATE/DELETE semantics correctly, manage long-running operations via polling, and never leave CloudFormation hanging.

cfn-handler does the boilerplate so you can focus on the resource logic.

from cfn_handler import CustomResource

resource = CustomResource()

@resource.create
def on_create(event, context):
    # do work, return data dict
    return {"Endpoint": "https://my.example.com"}

@resource.update
def on_update(event, context):
    return {"Endpoint": "https://my.example.com"}

@resource.delete
def on_delete(event, context):
    pass

def handler(event, context):
    return resource(event, context)

That's the entire happy path. For long-running operations:

@resource.create
def on_create(event, context):
    # kick off work; return None to defer
    pass

@resource.poll_create
def on_poll_create(event, context):
    # check status; return data when done, raise on failure
    if check_ready():
        return {"Endpoint": "https://my.example.com"}
    # else: do nothing, library will reschedule

Installation

pip install cfn-handler
# or with uv
uv add cfn-handler

cfn-handler requires Python 3.10+ and has zero runtime dependencies. Polling support uses boto3 lazily; boto3 ships preinstalled in the AWS Lambda Python runtimes, so no extra install is needed there.

Or use the AWS Lambda Layer

Every release publishes a public Lambda Layer in ~17 commercial regions. To use it, reference the ARN in your function definition — no pip install during deploy, no vendoring into your function package:

# SAM
Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.12
      Layers:
        - arn:aws:lambda:us-east-1:<account-id>:layer:cfn-handler:N

Find the right ARN for your region in the latest release notes (per-region table) or the JSON manifest:

curl -fsSL https://github.com/igorlg/cfn-handler/releases/latest/download/layer-arns.json

See layer/README.md for SAM/CDK snippets, the alternative deploy-it-yourself path, and the maintainer's account ID.

Comparison to crhelper

If you're coming from crhelper, the model will feel familiar:

Feature crhelper cfn-handler
Lifecycle decorators @helper.create, @helper.update, @helper.delete @resource.create, @resource.update, @resource.delete
Polling decorators @helper.poll_create ... @resource.poll_create ...
Type hints None Full inline (py.typed)
Python versions 3.6+ (last release 2020) 3.10–3.14
Build setup.py pyproject.toml (PEP 621, hatchling)
Tests unittest pytest + hypothesis
Coverage gate None 95% line + branch
Type checkers None mypy strict + pyright strict
Releases Manual release-please + PyPI Trusted Publishing
Maintained No (since 2020) Yes

The public API is intentionally similar — cfn-handler is a clean re-implementation that carries forward the proven semantics and fixes 14 long-standing upstream issues that never merged. See CHANGELOG.md for the full list.

Examples

Working SAM-deployable examples live in examples/:

  • examples/basic/ — minimal Create/Update/Delete handler.
  • examples/polled/ — long-running operation with polling.
  • examples/with-physical-id/ — explicit physical resource id (replacement on update).
  • examples/failing/ — handler that fails, demonstrating FAILED-response semantics.

Testing your handlers

cfn-handler ships first-class testing helpers under cfn_handler.testing. The dispatch flow runs in-process; no HTTP, no boto3, no moto setup required for unit tests:

from cfn_handler import CustomResource
from cfn_handler.testing import assert_success, make_event

def test_my_create_handler():
    resource = CustomResource()

    @resource.create
    def on_create(event, context):
        return {"Endpoint": "https://x.example"}

    replay = resource.replay(make_event())
    assert_success(replay, data={"Endpoint": "https://x.example"})

Available surface:

  • CustomResource.replay(event, context=None) — execute the dispatch in-process, returning a structured Replay (status, data, reason, payload, ...).
  • make_event(...), make_context(...) — factories with safe defaults.
  • assert_success, assert_failed, assert_deferred — assertion helpers with informative messages on failure.
  • pytest fixtures cfn_create_event, cfn_update_event, cfn_delete_event, cfn_lambda_context — auto-discovered via the pytest11 entry point; no pytest_plugins declaration needed.

For long-running (polled) handlers, the first replay() returns Replay(status="DEFERRED") and mutates the event with marker keys. A second replay() with the mutated event resumes through the poll handler — useful for testing both halves of a polled lifecycle without provisioning EventBridge rules.

Project status

v1.0.0 — first stable release. Follows Semantic Versioning. The public API surface is exactly what's exported from cfn_handler.__all__; everything under cfn_handler._internal is implementation detail and may change between minor versions.

Contributing

See CONTRIBUTING.md for the development workflow, commit conventions, and lockfile policy. For contributors who use Nix, a flake.nix provides a reproducible dev shell. The CI/release pipeline itself is documented in docs/CI.md, including the local-replay tooling and a postmortem of the v1.0.0 release failure.

License

Apache License 2.0 — see LICENSE and NOTICE.

This project is inspired by, but does not include code from, aws-cloudformation/custom-resource-helper. Both projects are licensed under Apache 2.0.

About

Modern CloudFormation Custom Resource lifecycle handler for AWS Lambda

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages