{"id":29554992,"url":"https://github.com/jftuga/cloudformation-s3-auto-empty-hook","last_synced_at":"2026-02-06T03:02:27.062Z","repository":{"id":284864112,"uuid":"956297491","full_name":"jftuga/cloudformation-s3-auto-empty-hook","owner":"jftuga","description":"CloudFormation Lambda hook to automatically empty S3 buckets before deletion","archived":false,"fork":false,"pushed_at":"2025-03-28T12:16:41.000Z","size":13,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-18T10:54:53.851Z","etag":null,"topics":["aws","ci","cloudformation","lambda-functions","lambda-hook","s3","s3-bucket"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jftuga.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-28T02:47:10.000Z","updated_at":"2025-03-28T12:18:39.000Z","dependencies_parsed_at":"2025-03-28T04:27:43.751Z","dependency_job_id":"c233ca4d-f462-4c30-9976-fdb944382ef0","html_url":"https://github.com/jftuga/cloudformation-s3-auto-empty-hook","commit_stats":null,"previous_names":["jftuga/cloudformation-s3-auto-empty-hook"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jftuga/cloudformation-s3-auto-empty-hook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jftuga%2Fcloudformation-s3-auto-empty-hook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jftuga%2Fcloudformation-s3-auto-empty-hook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jftuga%2Fcloudformation-s3-auto-empty-hook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jftuga%2Fcloudformation-s3-auto-empty-hook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jftuga","download_url":"https://codeload.github.com/jftuga/cloudformation-s3-auto-empty-hook/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jftuga%2Fcloudformation-s3-auto-empty-hook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29147385,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T02:39:25.012Z","status":"ssl_error","status_checked_at":"2026-02-06T02:37:22.784Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","ci","cloudformation","lambda-functions","lambda-hook","s3","s3-bucket"],"created_at":"2025-07-18T08:37:43.150Z","updated_at":"2026-02-06T03:02:27.023Z","avatar_url":"https://github.com/jftuga.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CloudFormation S3 Auto-Empty Hook\n\nCloudFormation Lambda hook to automatically empty S3 buckets before\ndeletion.\n\n## Overview\n\nThis repository provides a solution to a common AWS CloudFormation\nchallenge: deleting stacks that contain non-empty S3 buckets.\nNormally, CloudFormation cannot delete a bucket that contains\nobjects, requiring manual intervention to empty the bucket before\nstack deletion can succeed.\n\nThis solution implements a [CloudFormation Lambda Hook](https://docs.aws.amazon.com/cloudformation-cli/latest/hooks-userguide/lambda-hooks.html)\nthat automatically empties S3 buckets before CloudFormation attempts to delete them,\nenabling fully automated stack deletion.\n\n## How It Works\n\nWhen you initiate deletion of a CloudFormation stack containing an S3 bucket:\n\n1. CloudFormation triggers the Lambda hook before attempting to delete the bucket\n2. The Lambda function empties the bucket by deleting all objects and versions\n3. Once empty, CloudFormation proceeds with deleting the bucket itself\n\n## Security Considerations\n\nThe Lambda function requires permissions to empty any S3 bucket in\nyour AWS account. While this is broader than the principle of least\nprivilege might suggest, the function includes several security\nmeasures:\n\n1. Validation that it's only being called by CloudFormation Hooks\n2. Verification that it's only used for DELETE operations\n3. AWS account-level restrictions via the `aws:ResourceAccount` condition\n4. Limited functionality (can only empty buckets, not access or modify data)\n\n## Architecture\n\nThe solution uses a [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns)\napproach with two distinct CloudFormation stacks:\n\n1. **Lambda Hook Infrastructure** [lambda-hook-infrastructure.yaml](lambda-hook-infrastructure.yaml):\nA long-lived, foundational stack that creates the Lambda function\nand IAM roles needed for the hook mechanism. This stack is deployed\nonce and can be reused across multiple bucket stacks.\n2. **S3 Bucket Resources** [s3-bucket-resources.yaml](s3-bucket-resources.yaml): Contains\nthe actual S3 bucket and the hook configuration that references the\nLambda. This stack can be deployed to multiple environments.\n\n```mermaid\nflowchart TD\n    subgraph LambdaStack[\"Lambda Hook Infrastructure Stack\"]\n        Lambda[\"Lambda Function\n(Empty S3 Bucket)\"]\n        IAMRole[\"IAM Role\n(Hook Invoker)\"]\n        LogGroup[\"CloudWatch\nLog Group\"]\n    end\n\n    subgraph BucketStack[\"S3 Bucket Resources Stack\"]\n        S3Bucket[\"S3 Bucket\"]\n        CFNHook[\"CloudFormation\nLambda Hook\"]\n    end\n\n    User[\"User/CI System\"]\n    CFN[\"CloudFormation\nService\"]\n\n    User --\u003e|\"Initiates stack\ndeletion\"| CFN\n    CFN --\u003e|\"Processes\ndeletion request\"| CFNHook\n\n    CFNHook --\u003e|\"#1 Invokes before\nbucket deletion\"| IAMRole\n    IAMRole --\u003e|\"#2 Assumes role to\ninvoke Lambda\"| Lambda\n    Lambda --\u003e|\"#3 Empties bucket\n(DELETE operations)\"| S3Bucket\n    Lambda --\u003e|\"Logs operations\"| LogGroup\n\n    CFN --\u003e|\"#4 Deletes empty\nbucket\"| S3Bucket\n\n    style User fill:#D86613,stroke:#D86613,color:white\n    style CFN fill:#FF9900,stroke:#232F3E,color:white\n    style S3Bucket fill:#FF9900,stroke:#232F3E,color:white\n    style Lambda fill:#3B48CC,stroke:#3B48CC,color:white\n    style IAMRole fill:#3B48CC,stroke:#3B48CC,color:white\n    style CFNHook fill:#3B48CC,stroke:#3B48CC,color:white\n    style LogGroup fill:#3B48CC,stroke:#3B48CC,color:white\n    style LambdaStack fill:#147EBA,stroke:#147EBA,color:white\n    style BucketStack fill:#147EBA,stroke:#147EBA,color:white\n```\n\n## Components\n\n- **Lambda Function**: Automatically empties S3 buckets by deleting all objects and object versions\n- **IAM Role**: Allows CloudFormation to invoke the Lambda function\n- **CloudFormation Lambda Hook**: Configures when the Lambda function should be triggered\n- - **Note**: The Hook's [Alias property](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-lambdahook.html#cfn-cloudformation-lambdahook-alias)\n uses the `StackShortName` parameter to create a unique identifier following the required\n pattern `Name1::Name2::Name3`. This parameter must be carefully chosen to ensure the Alias\n meets CloudFormation's naming requirements and **remains unique per account and Region**.\n- **S3 Bucket**: Standard S3 bucket with security best practices enabled\n\n- **S3 Bucket**: Standard S3 bucket with security best practices enabled\n\n## Prerequisites\n\n- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) configured with appropriate permissions\n- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) installed\n- Python 3.13 or compatible version\n- [ruff](https://docs.astral.sh/ruff) tool for Python code linting\n\n## Deployment\n\nThe deployment script, [deploy-stacks.sh](deploy-stacks.sh), supports several options:\n\n```bash\n# Deploy both stacks\n./deploy-stacks.sh\n\n# Deploy only the hook infrastructure\n./deploy-stacks.sh --hook-only\n\n# Deploy only the bucket resources\n./deploy-stacks.sh --bucket-only\n\n# Use a specific AWS profile\n./deploy-stacks.sh --profile prod\n\n# Deploy to a specific region\n./deploy-stacks.sh --region us-west-2\n```\n\n### Configuration\n\nEdit the [.env](.env) file to customize:\n\n- AWS profile and region\n- S3 bucket name prefix\n- Hook configuration settings\n- Deployment environment tag\n\n## Testing\n\nThe repository includes an [upload-test-files.sh](upload-test-files.sh) script to help\ntest the S3 bucket emptying functionality:\n\n```bash\n# Upload test files with multiple versions\n./upload-test-files.sh\n\n# Specify a custom bucket\n./upload-test-files.sh --bucket my-custom-bucket\n\n# Create more versions (default is 3)\n./upload-test-files.sh --iterations 5\n```\n\n## Customization\n\nYou can customize this solution by:\n\n- Modifying the Lambda function for specific bucket handling logic\n- Adding additional validation or notification logic to the emptying process\n- Adjusting IAM permissions to be more restrictive if needed\n\n## Comparison with DeletionPolicy\n\nAWS CloudFormation's native `DeletionPolicy` attribute offers alternatives:\n\n- **DeletionPolicy: Retain**: Leaves the bucket intact when the stack is deleted (manual cleanup required)\n- **DeletionPolicy: Delete** (Default): Attempts to delete the bucket but fails if it contains objects\n\nThis Lambda Hook solution provides automated cleanup while still\nperforming a complete deletion, combining the best of both approaches.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjftuga%2Fcloudformation-s3-auto-empty-hook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjftuga%2Fcloudformation-s3-auto-empty-hook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjftuga%2Fcloudformation-s3-auto-empty-hook/lists"}