Introduction
We've all been there: you hand a developer a sandbox account, they spend two weeks spinning up VPCs, RDS instances, half-finished EKS clusters and a Bedrock experiment nobody remembers, and now you want it clean again. Not gone, not re-vended through Control Tower, just clean. Reset to the same baseline every other account has.
The obvious tool for that is aws-nuke. Point it at an account, and it removes everything it can find. I use ekristen's fork (as the original one from rebuy-de is no longer maintained), currently on the v3 line.
But "everything" is exactly the problem. If your accounts are governed by AWS Control Tower and managed by the Landing Zone Accelerator (LZA), a naive nuke run happily deletes the AWSControlTowerExecution role, the AWSReservedSSO_* roles from IAM Identity Center, and every AWSAccelerator-* resource the pipeline put there. The account survives, but it has drifted out of governance, and the next LZA pipeline run fails when attempting to re-baseline it manually.
So the trick is not running nuke, it is telling it which resources to keep with the appropriate rules.
Let's jump in.
The challenge
When Control Tower vends an account and LZA baselines it, three sets of resources land in that account that you must never touch:
- Control Tower baseline: the
AWSControlTowerExecutionrole, config recorders, the org CloudTrail, the guardrail plumbing (all named or taggedaws-controltower/AWSControlTower). - IAM Identity Center (SSO): the
AWSReservedSSO_*permission-set roles and theAWSSSO_..._DO_NOT_DELETESAML provider. - Landing Zone Accelerator: everything the pipeline deploys, named or tagged
AWSAccelerator,aws-accelerator, orcdk-accel(the CDK bootstrap it uses). KMS keys, S3 buckets, Lambda functions, CloudFormation stacks, SSM parameters, Config rules, the lot.
Delete any of those, and the account breaks in a way that is annoying to fix. Also, it is highly possible that deleting those resources is protected by SCPs, and you cannot delete them even with an administrator role. Re-registering an account in Control Tower or forcing an LZA re-baseline is a lot more work than the nuke was supposed to save you.
Now, aws-nuke has a presets feature for exactly this: named blocks of filters you attach to an account so matching resources are skipped. And the community config presets already ship an sso preset and a controltower preset. Good start.
However there is a gap: there is no LZA preset in the community docs. If you nuke an LZA-managed account with only sso and controltower filters, the accelerator's entire footprint is in scope for deletion. That is the missing piece.
The solution
The best LZA preset I found is not in the aws-nuke docs at all, it is buried in an AWS sample repo: the default-nuke-config-template.yml from sandbox-accounts-for-events (the old Disposable Cloud Environment / DCE project). It runs nuke on a schedule to reset event sandbox accounts, and it carries an lza preset alongside the sso and controltower ones.
That file is a Go template rendered per-account inside CodeBuild ({{ .ID }}, {{ .Regions }} and friends). Below is the same thing de-templated into a standalone config you can run yourself.
Install
Grab the binary from the releases page, or on macOS use the author's tap:
brew install ekristen/tap/aws-nukeCheck you are on a v3 build:
aws-nuke --versionThe config
Two safety rails first, because nuke is unforgiving:
account-blocklistis mandatory. Put your management account (and any other account you never want touched) here. If the account you are authenticated to appears on the blocklist, nuke refuses to run. This is your seatbelt against pointing it at the wrong account.- Runs are a dry run by default. You only delete for real when you pass
--no-dry-run. Keep it that way until the output looks right.
regions:
- "global" # global services: IAM, Route 53, CloudFront, ...
- "us-east-1" # keep this even for eu workloads: many global APIs live here
- "eu-central-1" # add EVERY region you ever deployed into
account-blocklist:
- "123456789012" # your management account - NEVER nuke this
resource-types:
excludes:
- S3Object # let S3Bucket delete its own objects (much faster)
- GuardDutyDetector # part of the org GuardDuty integration
- SecurityHub # same, org-managed
- OSPackage # avoids non-nukeable AWS-managed OpenSearch packages
accounts:
"210987654321": # the sandbox account you are resetting
presets:
- "sso"
- "controltower"
- "lza"
filters:
# keep the cross-account access role Organizations creates
IAMRole:
- type: "contains"
value: "OrganizationAccountAccessRole"
IAMRolePolicy:
- property: RoleName
value: "OrganizationAccountAccessRole"
IAMRolePolicyAttachment:
- property: RoleName
value: "OrganizationAccountAccessRole"Then the presets. First the two from the community docs, sso and controltower:
presets:
sso:
filters:
IAMSAMLProvider:
- type: "regex"
value: "AWSSSO_.*_DO_NOT_DELETE"
IAMRole:
- type: "glob"
value: "AWSReservedSSO_*"
IAMRolePolicyAttachment:
- type: "glob"
value: "AWSReservedSSO_*"
IAMRolePolicy:
- type: "glob"
value: "AWSReservedSSO_*"
controltower:
filters:
CloudTrailTrail:
- type: "contains"
value: "aws-controltower"
CloudWatchEventsRule:
- type: "contains"
value: "aws-controltower"
EC2VPC:
- type: "contains"
value: "aws-controltower"
- property: tag:Name
type: "contains"
value: "aws-controltower"
EC2Subnet:
- type: "contains"
value: "aws-controltower"
- property: tag:Name
type: "contains"
value: "aws-controltower"
EC2RouteTable:
- type: "contains"
value: "aws-controltower"
OpsWorksUserProfile:
- type: "contains"
value: "AWSControlTowerExecution"
CloudWatchLogsLogGroup:
- type: "contains"
value: "aws-controltower"
- type: "contains"
value: "AWSControlTowerBP"
ConfigServiceDeliveryChannel:
- type: "contains"
value: "aws-controltower"
ConfigServiceConfigurationRecorder:
- type: "contains"
value: "aws-controltower"
CloudFormationStack:
- type: "contains"
value: "AWSControlTower"
LambdaFunction:
- type: "contains"
value: "aws-controltower"
IAMRole:
- type: "contains"
value: "aws-controltower"
- type: "contains"
value: "AWSControlTower"
IAMRolePolicyAttachment:
- type: "contains"
value: "aws-controltower"
- type: "contains"
value: "AWSControlTower"
IAMRolePolicy:
- type: "contains"
value: "aws-controltower"
- type: "contains"
value: "AWSControlTower"And the part that makes this work for LZA is the lza preset. Every LZA resource is matched three ways: by the AWSAccelerator name, the aws-accelerator name, and the cdk-accel CDK bootstrap prefix, plus the Accelerator tag the pipeline stamps on things:
lza:
filters:
IAMRole: &lza
- type: "contains"
value: "AWSAccelerator"
- type: "contains"
value: "aws-accelerator"
- type: "contains"
value: "cdk-accel"
- property: tag:Accelerator
type: "contains"
value: "AWSAccelerator"
IAMPolicy: *lza
IAMRolePolicy: *lza
IAMRolePolicyAttachment: *lza
CloudFormationStack: *lza
LambdaFunction: *lza
S3Bucket: *lza
KMSKey: *lza
KMSAlias: *lza
SSMParameter: *lza
SNSTopic: *lza
SNSSubscription: *lza
CloudWatchEventsRule: *lza
CloudWatchEventsTarget: *lza
CloudWatchLogsLogGroup: *lza
ConfigServiceConfigRule: *lza
AWSBackupVault: *lzaI used a YAML anchor (&lza / *lza) here because the filter is identical for every resource type. The DCE (Disposable cloud environments) original spells each one out in full; if your tooling does not like anchors, expand it back to one block per resource type. The set of resource types is the important part, that is the full list of things LZA creates in a member account.
Dry run, then nuke
Preview what nuke would do without deleting anything. The default run is already a dry run, but explain-config is even safer, it just prints the plan:
aws-nuke explain-config -c nuke-config.yamlThen a real dry run against the account (assume the role into the sandbox account first, e.g. via AWS_PROFILE):
aws-nuke run -c nuke-config.yaml --profile sandboxRead the output. Everything AWSAccelerator-*, aws-controltower* and AWSReservedSSO_* should be listed as filtered (skipped), and your workload junk should be listed as would remove. If a baseline resource appears under "would remove", stop and fix your filters before proceeding. Do not skip this step.
When it looks right, run it in explicit --no-dry-run mode:
aws-nuke run -c nuke-config.yaml --profile sandbox --no-dry-runnuke also refuses to run against an account with no account alias set, another guardrail to prevent you from accidentally targeting a fresh production account. It runs in loops, retrying resources whose dependencies still exist, until the account is drained down to the baseline you told it to keep.
Conclusion
The community sso and controltower presets get you most of the way, but the missing lza preset is what stops a sandbox reset from turning into an afternoon of re-baselining. The one I use comes straight out of the AWS sandbox-accounts-for-events template, de-templated so you can run it by hand instead of only inside their CodeBuild job.
Personally, my rule of thumb is: for a sandbox or throwaway dev account, reset with nuke rather than closing and re-vending it through Control Tower. Re-vending means a new email alias, a new account under the OU, and waiting on the pipeline; a filtered nuke run gets you a clean account in minutes and retains the same account ID. Keep the config in version control, treat the blocklist and the dry run as mandatory, and it becomes a boring, repeatable operation.
I hope this saves you the broken-baseline detour. Happy AWS account nuking, carefully.
This post was written with AI assistance and verified plus enhanced by a human.



