Skip to main 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.

Metadata

Release files for cfn-handler 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cfn-handler 1.3.0
File Size Uploaded Provenance Actions
cfn_handler-1.3.0.tar.gz 32.9 kB With provenance Details for cfn_handler-1.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cfn-handler 1.3.0
File Interpreter ABI Platform Actions
cfn_handler-1.3.0-py3-none-any.whl Python 3 none any Details for cfn_handler-1.3.0-py3-none-any.whl

Total release size: 66.7 kB

Release files / cfn_handler-1.3.0.tar.gz

Download URL cfn_handler-1.3.0.tar.gz
Size 32.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ddff3dea74843352ee4602a988fe4e6d24b214e50cc4f472729c3f1e4757900f
BLAKE2b-256 checksum
How to use checksums
39edba9072bdc96cb4d476bfd8350ac4252d385f6087fc97d75b600fe03d789b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 25, 2026.

Transparency log

Release files / cfn_handler-1.3.0-py3-none-any.whl

Download URL cfn_handler-1.3.0-py3-none-any.whl
Size 33.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1df4a0dc0cd51b2ccb9623904707c923b56ef2f2ea7b488541962730787f1df1
BLAKE2b-256 checksum
How to use checksums
8b27e07b75aefdca796de2f27c3d4a016b7eb8955101de42b7d40884cf7f7503
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 25, 2026.

Transparency log

Security for cfn-handler 1.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for cfn_handler-1.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page