The purpose of this module is to provide a method of automated cleanup of AWS resources, using the aws-nuke tool. It schedules periodic nuke runs to clean up resources that are no longer needed in non-production accounts (development, sandbox, testing).
The module supports two compute backends -- choose one or run both simultaneously:
- ECS Fargate (
var.ecs): Runs aws-nuke as a Fargate task. Uses the upstream aws-nuke container image directly. - Lambda (
var.lambda): Runs aws-nuke as a container-image Lambda function. Requires a Lambda-compatible wrapper image -- see Lambda Wrapper Image below.
Each compute backend accepts a tasks map where each entry defines a separate nuke configuration, schedule, and IAM permissions. Secrets Manager stores the nuke YAML configuration for each task.
- Dual compute backends -- ECS Fargate and/or Lambda, selectable via simple feature flags.
- Multi-task scheduling -- define multiple independent nuke tasks, each with its own configuration, cron schedule, and IAM permissions.
- Safe by default -- tasks default to
dry_run = true; destructive runs require explicit opt-in. - Configuration helper module -- the bundled
modules/configurationmodule renders aws-nuke YAML with built-in filters for Control Tower, Landing Zone Accelerator, AWS managed services, and Cost Intelligence Dashboard resources. - ECR repository module -- the
modules/repositorymodule provisions a private ECR repository with cross-account and organization-wide pull policies for the Lambda wrapper image. - SNS notifications -- optional per-task completion notifications via SNS (ECS triggers a Lambda notifier on task stop; Lambda publishes directly).
- KMS encryption -- optional KMS key creation for CloudWatch log group encryption.
- Secrets Manager integration -- nuke configuration YAML is stored securely and injected at runtime.
- Customisable IAM -- per-task managed policy attachments, inline policies, and permission boundaries.
module "nuke" {
source = "github.com/appvia/terraform-aws-nuke?ref=main"
account_id = data.aws_caller_identity.current.account_id
region = data.aws_region.current.name
tags = local.tags
## Configure ECS Fargate as the compute backend
ecs = {
subnet_ids = module.vpc.public_subnet_ids
}
tasks = {
"default" = {
configuration = file("${path.module}/assets/nuke-config.yml")
description = "Weekly nuke run — deletes sandbox resources"
dry_run = false
schedule = "cron(0 10 ? * FRI *)"
permission_arns = ["arn:aws:iam::aws:policy/AdministratorAccess"]
}
}
}Lambda mode requires a Lambda-compatible container image (see Lambda Wrapper Image).
module "nuke" {
source = "github.com/appvia/terraform-aws-nuke?ref=main"
account_id = data.aws_caller_identity.current.account_id
container_image = "<account_id>.dkr.ecr.<region>.amazonaws.com/lz/operations/aws-nuke:latest"
region = data.aws_region.current.name
tags = local.tags
## Configure Lambda as the compute backend
lambda = {
architecture = "arm64"
}
tasks = {
"default" = {
configuration = file("${path.module}/assets/nuke-config.yml")
description = "Weekly nuke run — deletes sandbox resources"
dry_run = false
schedule = "cron(0 10 ? * FRI *)"
permission_arns = ["arn:aws:iam::aws:policy/AdministratorAccess"]
}
}
}The upstream aws-nuke image is not Lambda-compatible (it has no Lambda Runtime Interface Client). For Lambda mode you must build and push a wrapper image.
A ready-made Dockerfile and Python handler (handler.py) are provided in assets/docker/. Build and push with:
You can change the defaults using environment variables:
| Name | Description | Default |
|---|---|---|
DOCKER_IMAGE |
ECR repository path/name for the Lambda-compatible wrapper image that is built and pushed. | ACCOUNT.dkr.ecr.eu-west-2.amazonaws.com/lz/rebuy/aws-nuke |
DOCKER_PLATFORM |
The architecture to use for the docker iamge | linux/amd64 |
NUKE_IMAGE |
Source aws-nuke container image used as the base for the Lambda-compatible wrapper image build. | ghcr.io/ekristen/aws-nuke |
NUKE_TAG |
Tag of the source aws-nuke image used in the wrapper image build. | v3.26.0-2-g672408a-amd64 |
# The tag of of the image will be the same as the NUKE_TAG
make docker-ecr-imageAnd to push the image
make docker-ecr-image-pushIf you prefer not to use the Terraform repository sub-module, you can create and configure the ECR repository manually with the AWS CLI.
export AWS_ECR_REGION="eu-west-2"
export AWS_ACCOUNT_ID="<your-account-id>"
export REPOSITORY_NAME="lz/operations/aws-nuke"
aws ecr create-repository \
--region "${AWS_ECR_REGION}" \
--repository-name "${REPOSITORY_NAME}"Create a policy file (for example ecr-policy.json) to allow pull access from all accounts in your AWS Organization:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOrgPullAccess",
"Effect": "Allow",
"Principal": "*",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
],
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-7u80c4XXXX"
}
}
},
{
"Sid": "AllowLambdaServiceImageRetrieval",
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
],
"Condition": {
"StringLike": {
"aws:sourceArn": "arn:aws:lambda:eu-west-2:*:function:*"
}
}
}
]
}Apply the repository policy:
aws ecr set-repository-policy \
--region "${AWS_ECR_REGION}" \
--repository-name "${REPOSITORY_NAME}" \
--policy-text file://ecr-policy.jsonBuild and push the Lambda wrapper image while overriding DOCKER_IMAGE:
DOCKER_IMAGE="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_ECR_REGION}.amazonaws.com/${REPOSITORY_NAME}" make docker-ecr-image-pushUse the modules/repository sub-module to provision the ECR repository:
module "nuke_ecr" {
source = "github.com/appvia/terraform-aws-nuke//modules/repository?ref=main"
repository_name = "lz/operations/aws-nuke"
account_ids = ["123456789012"]
tags = local.tags
}- Basic (ECS Fargate) -- deploys aws-nuke with ECS Fargate, including a destructive run and a dry-run with SNS notification.
- Lambda -- deploys aws-nuke with a Lambda container-image backend.
The repository also includes a helper configuration module that renders a nuke configuration file. An example of how to use the module:
module "configuration" {
source = "github.com/appvia/terraform-aws-nuke//modules/configuration?ref=main"
accounts = ["123456789012", "123456789013"]
regions = ["us-east-1", "us-west-2"]
filters = [
{
property = "tag:Environment"
type = "string"
value = "Sandbox"
}
]
}
module "nuke" {
source = "github.com/appvia/terraform-aws-nuke?ref=main"
account_id = data.aws_caller_identity.current.account_id
region = data.aws_region.current.name
tags = local.tags
ecs = {
subnet_ids = module.vpc.public_subnet_ids
}
tasks = {
"default" = {
configuration = module.configuration.configuration
description = "Weekly nuke run"
dry_run = false
schedule = "cron(0 10 ? * FRI *)"
permission_arns = ["arn:aws:iam::aws:policy/AdministratorAccess"]
}
}
}The terraform-docs utility is used to generate this README. Follow the below steps to update:
- Make changes to the
.terraform-docs.ymlfile - Fetch the
terraform-docsbinary (https://terraform-docs.io/user-guide/installation/) - Run
terraform-docs markdown table --output-file ${PWD}/README.md --output-mode inject .
| Name | Version |
|---|---|
| aws | >= 6.0.0 |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| account_id | The account id to use for the resources | string |
n/a | yes |
| region | The region to use for the resources | string |
n/a | yes |
| tasks | A collection of nuke tasks to run and when to run them | map(object({ |
n/a | yes |
| configuration_secret_name_prefix | The prefix to use for AWS Secrets Manager secrets to store the nuke configuration | string |
"/lz/services/nuke" |
no |
| container_image | The image to use for the container | string |
"ghcr.io/ekristen/aws-nuke" |
no |
| container_image_tag | The tag to use for the container image | string |
"v3.26.0-2-g672408a-amd64" |
no |
| create_kms_key | Indicates if a KMS key should be created for the log group | bool |
false |
no |
| ecs | Indicates if the ECS cluster should be created | object({ |
null |
no |
| kms_administrator_role_name | The name of the role to use as the administrator for the KMS key (defaults to account root) | string |
"" |
no |
| lambda | Indicates if the Lambda function should be created | object({ |
null |
no |
| name | The name of the instance (used to prefix the resources) | string |
"lz-nuke" |
no |
| tags | Map of tags to apply to resources created by this module | map(string) |
{} |
no |
| Name | Description |
|---|---|
| ecs_cluster_arn | The ARN of the ECS cluster running nuke tasks, if ECS mode is enabled |
| ecs_cluster_name | The name of the ECS cluster running nuke tasks, if ECS mode is enabled |
| kms_key_arn | The KMS key ARN used for the nuke service, if created |
| kms_key_id | The KMS key ID used for the nuke service, if created |
| lambda_function_arn | The ARN of the Lambda function running nuke tasks, if serverless mode is enabled |
| lambda_function_name | The name of the Lambda function running nuke tasks, if serverless mode is enabled |
| secret_arns | A map of task name to the ARN of the SecretsManager secret holding the nuke configuration |
