Test a handler with saved requests. This is the Create call CloudFormation 24 would send, except that its ResponseURL is an object in LocalStack 63,725 's S3 24 , which accepts unsigned PUTs:
{"RequestType": "Create", "RequestId": "req-0001", "LogicalResourceId": "SeedBooks",
"StackId": "arn:aws:cloudformation:us-east-1:000000000000:stack/booknest-seed/manual",
"ResponseURL": "http://s3.localhost.localstack.cloud:4566/booknest-templates/responses/req-0001",
"ResourceType": "Custom::BookSeed", "ResourceProperties": {"TableName": "booknest-seeded-books"}}aws lambda invoke --function-name booknest-seeder --cli-binary-format raw-in-base64-out \
--payload file://events/seed-create.json /dev/null --query StatusCode
aws s3 cp s3://booknest-templates/responses/req-0001 - | jq -c '{Status, PhysicalResourceId, Data}'
BAD=$(jq -c '.RequestId = "req-0002" | .ResponseURL |= sub("0001"; "0002")
| .ResourceProperties.TableName = "booknest-missing"' events/seed-create.json)
aws lambda invoke --function-name booknest-seeder --cli-binary-format raw-in-base64-out \
--payload "$BAD" /dev/null --query StatusCode
aws s3 cp s3://booknest-templates/responses/req-0002 - | jq -c '{Status, Reason}'200
{"Status":"SUCCESS","PhysicalResourceId":"seed-booknest-seeded-books","Data":{"Count":6}}
200
{"Status":"FAILED","Reason":"ResourceNotFoundException: Cannot do operations on a non-existent
table"}The first call seeded the books and answered SUCCESS with the Count behind !GetAtt SeedBooks.Count. The second failed at DynamoDB 24 , yet Lambda 24 returned 200: the catch turned the error into a FAILED answer whose Reason appears in the stack events. That catch matters most: a handler that throws or times out before respond() sends nothing, and the stack waits out the full ServiceTimeout. Also handle a Delete for a resource whose Create failed, since rollback still sends one, and keep every request idempotent, since CloudFormation may repeat it.