{"id":5890,"library":"crhelper","title":"Custom Resource Helper (crhelper)","description":"crhelper simplifies authoring CloudFormation Custom Resources, implementing best practices for handling responses to CloudFormation, exception and timeout trapping, and detailed configurable logging. It is an open-source project actively maintained by AWS CloudFormation. The current version is 2.0.12, with regular updates to its PyPI package.","status":"active","version":"2.0.12","language":"en","source_language":"en","source_url":"https://github.com/aws-cloudformation/custom-resource-helper","tags":["aws","cloudformation","lambda","custom-resource","serverless"],"install":[{"cmd":"pip install crhelper","lang":"bash","label":"Standard Installation"},{"cmd":"pip install -t . crhelper","lang":"bash","label":"Installation for AWS Lambda"}],"dependencies":[{"reason":"crhelper leverages boto3 internally for AWS service interactions, and most custom resource logic will require it for resource management.","package":"boto3","optional":false}],"imports":[{"note":"The primary class for managing custom resource interactions with CloudFormation.","symbol":"CfnResource","correct":"from crhelper import CfnResource"}],"quickstart":{"code":"import logging\nimport os\nfrom crhelper import CfnResource\n\nlogger = logging.getLogger(__name__)\n# Initialise the helper, all inputs are optional\nhelper = CfnResource(json_logging=True, log_level='INFO', boto_level='CRITICAL')\n\n# Example function to be called by the helper\n@helper.create\n@helper.update\ndef create_update_resource(event, context):\n    logger.info(\"Got Create/Update event\")\n    # Access properties from the CloudFormation event\n    my_property = event['ResourceProperties'].get('MyProperty', 'default')\n    logger.info(f\"MyProperty: {my_property}\")\n    \n    # Simulate creating/updating a resource\n    physical_resource_id = f\"my-custom-resource-{my_property}\"\n    \n    # Optionally return an ID that will be used for the PhysicalResourceId\n    # if None is returned, an ID will be generated. The value is used in subsequent Update/Delete events.\n    helper.Data['Result'] = 'Success'\n    return physical_resource_id\n\n@helper.delete\ndef delete_resource(event, context):\n    logger.info(\"Got Delete event\")\n    # Access the PhysicalResourceId of the resource to be deleted\n    physical_resource_id = event.get('PhysicalResourceId')\n    logger.info(f\"Deleting resource: {physical_resource_id}\")\n    # Simulate deleting the resource\n    # No return value needed for delete\n\ndef handler(event, context):\n    logger.info(\"Lambda handler invoked\")\n    helper(event, context)\n\n# Example of how to run this locally for testing purposes (outside Lambda)\nif __name__ == '__main__':\n    # Mock event and context for local testing\n    mock_event = {\n        'RequestType': 'Create',\n        'ResponseURL': 'http://example.com/',\n        'StackId': 'arn:aws:cloudformation:us-east-1:123456789012:stack/MyStack/UUID',\n        'RequestId': 'uniqueid123',\n        'LogicalResourceId': 'MyCustomResource',\n        'ResourceType': 'Custom::MyResource',\n        'ResourceProperties': {\n            'ServiceToken': 'arn:aws:lambda:us-east-1:123456789012:function:my-cr-lambda',\n            'MyProperty': 'test-value'\n        }\n    }\n    mock_context = type('Context', (object,), {\n        'function_name': 'test-func',\n        'invoked_function_arn': 'arn:aws:lambda:us-east-1:123456789012:function:test-func',\n        'aws_request_id': 'reqid123',\n        'log_group_name': '/aws/lambda/test-func',\n        'log_stream_name': '2026/04/14/[$LATEST]uuid',\n        'memory_limit_in_mb': '128',\n        'get_remaining_time_in_millis': lambda: 60000 # Mock 60 seconds remaining\n    })()\n    \n    print(\"--- Simulating Create Event ---\")\n    # In a real scenario, helper would send response to ResponseURL\n    # For local test, we just call the handler and check logs\n    handler(mock_event, mock_context)\n\n    print(\"\\n--- Simulating Update Event ---\")\n    mock_event['RequestType'] = 'Update'\n    mock_event['PhysicalResourceId'] = 'my-custom-resource-test-value' # Must be present for Update/Delete\n    handler(mock_event, mock_context)\n\n    print(\"\\n--- Simulating Delete Event ---\")\n    mock_event['RequestType'] = 'Delete'\n    handler(mock_event, mock_context)","lang":"python","description":"This quickstart demonstrates a typical `crhelper` integration in an AWS Lambda function. It initializes `CfnResource` and defines handler functions for `Create`, `Update`, and `Delete` events using decorators. It also includes a basic local test setup."},"warnings":[{"fix":"When using AWS CDK, define custom resources directly using `CustomResource` and pass the Lambda function's ARN as the `ServiceToken` instead of relying on the `Provider` construct.","message":"crhelper is explicitly not intended for use with AWS CDK's `Provider` construct. Using it this way can lead to unexpected behavior or conflicts.","severity":"gotcha","affected_versions":"2.0.0+"},{"fix":"Mock the `crhelper._send_response` method during local testing to prevent external calls. Alternatively, ensure the `AWS_SAM_LOCAL` environment variable is set to enable internal `crhelper` logic that may prevent actual network calls for responses.","message":"When testing locally (e.g., with `sam local`), `crhelper` attempts to send a response to CloudFormation, which fails if no actual stack or pre-signed URL exists. This can obscure actual business logic errors.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure that if `init_failure` is called during a `create` event, the `PhysicalResourceId` is explicitly set to `None` if no resource was truly created, or provide a meaningful, idempotent `PhysicalResourceId` if a partial creation occurred that needs cleanup.","message":"If a `create` event handler fails (e.g., due to `helper.init_failure`), `crhelper` might not generate or propagate a `PhysicalResourceId` correctly, which can lead to CloudFormation rollback issues (e.g., attempting a `delete` on a non-existent resource).","severity":"gotcha","affected_versions":"All versions"},{"fix":"Attach the necessary IAM policy to the Lambda function's role as described in the `crhelper` documentation, including actions like `lambda:AddPermission`, `events:PutRule`, `events:DeleteRule`, `events:PutTargets`, and `events:RemoveTargets` on `*` resources.","message":"Enabling the polling feature for long-running custom resources requires additional IAM permissions for the Lambda function's execution role to manage Lambda permissions and EventBridge rules. Failing to provide these will cause polling to fail.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-14T00:00:00.000Z","next_check":"2026-07-13T00:00:00.000Z","problems":[]}