Skip to content

Conversation

gsoria
Copy link

@gsoria gsoria commented Mar 7, 2025

CloudFormation stack RoleARN is not required when creating a stack. When a ValidationError was happening because the stack had set the TerminationProtection, the first condition evaluated was if a role was invalid or cannot be assumed, but since the stack was created without a role, this caused a nil pointer dereference error, and the UpdateTerminationProtection was never executed.

Do you really want to nuke the account with the ID 384736907310 and the alias 'alias-384736907310'?
Do you want to continue? Enter account alias to continue.
> alias-384736907310

ERRO[0020] CloudFormationStack stackName=MyStack attempt=0 maxAttempts=3 delete failed: ValidationError: Stack [MyStack] cannot be deleted while TerminationProtection is enabled
        status code: 400, request id: a709aab6-c576-45ff-a26a-491bb64522bf  component=scanner region=us-east-1 resource=CloudFormationType
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x5db3da0]

goroutine 1 [running]:
main.main.func1()
        /workspaces/aws-nuke-v2/main.go:28 +0xa4
panic({0x7038340?, 0xcb18420?})
        /usr/local/go/src/runtime/panic.go:785 +0xf0
github.com/ekristen/aws-nuke/v3/resources.(*CloudFormationStack).removeWithAttempts(0x400099a140, {0x8aac488, 0x40004c0190}, 0x0)
        /workspaces/aws-nuke-v2/resources/cloudformation-stack.go:178 +0x4e0
github.com/ekristen/aws-nuke/v3/resources.(*CloudFormationStack).Remove(0x400099a140, {0x8aac488, 0x40004c0190})
        /workspaces/aws-nuke-v2/resources/cloudformation-stack.go:126 +0x3c
github.com/ekristen/libnuke/pkg/nuke.(*Nuke).HandleRemove(0x4000a22320, {0x8aac488, 0x40004c0190}, 0x400072c300)
        /go/pkg/mod/github.com/ekristen/[email protected]/pkg/nuke/nuke.go:607 +0x40
github.com/ekristen/libnuke/pkg/nuke.(*Nuke).HandleQueue(0x4000a22320, {0x8aac488, 0x40004c0190})
        /go/pkg/mod/github.com/ekristen/[email protected]/pkg/nuke/nuke.go:562 +0x1a0
github.com/ekristen/libnuke/pkg/nuke.(*Nuke).run(0x4000a22320, {0x8aac488, 0x40004c0190})
        /go/pkg/mod/github.com/ekristen/[email protected]/pkg/nuke/nuke.go:319 +0x78
github.com/ekristen/libnuke/pkg/nuke.(*Nuke).Run(0x4000a22320, {0x8aac488, 0x40004c0190})
        /go/pkg/mod/github.com/ekristen/[email protected]/pkg/nuke/nuke.go:225 +0x2b8
github.com/ekristen/aws-nuke/v3/pkg/commands/nuke.execute(0x4000199800)
        /workspaces/aws-nuke-v2/pkg/commands/nuke/nuke.go:238 +0x1c04
github.com/urfave/cli/v2.(*Command).Run(0x40004e6420, 0x4000199800, {0x4000199840, 0x4, 0x4})
        /go/pkg/mod/github.com/urfave/cli/[email protected]/command.go:276 +0xd00
github.com/urfave/cli/v2.(*Command).Run(0x40004e69a0, 0x4000199680, {0x40000700a0, 0x5, 0x5})
        /go/pkg/mod/github.com/urfave/cli/[email protected]/command.go:269 +0xc60
github.com/urfave/cli/v2.(*App).RunContext(0x4000562000, {0x8aac530, 0xcb69ae0}, {0x40000700a0, 0x5, 0x5})
        /go/pkg/mod/github.com/urfave/cli/[email protected]/app.go:333 +0x238
github.com/urfave/cli/v2.(*App).Run(0x4000562000, {0x40000700a0, 0x5, 0x5})
        /go/pkg/mod/github.com/urfave/cli/[email protected]/app.go:307 +0x60
main.main()
        /workspaces/aws-nuke-v2/main.go:50 +0x2f0

Testing

  • To facilitate the local tests, configure devcontainer file: devcontainer.json:
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
	"name": "Go",
	// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
	"image": "mcr.microsoft.com/devcontainers/go:1-1.23-bookworm",
	// "features": {
	// 	"ghcr.io/guiyomh/features/goreleaser:0": {}
	// }

	// Features to add to the dev container. More info: https://containers.dev/features.
	// "features": {},

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],

	// Use 'postCreateCommand' to run commands after the container is created.
	// "postCreateCommand": "go version",

	// Configure tool-specific properties.
	// "customizations": {},

	// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
	// "remoteUser": "root"
}
  • Configure .env file:
# Update these values with the output of the assume-role command:
aws sts assume-role \
--role-arn arn:aws:iam::384736907310:role/cloudlabs/CloudLabsCleanupRole \
--role-session-name MySession \
--query "Credentials.[AccessKeyId,SecretAccessKey,SessionToken]" \
--output text

AWS_ACCESS_KEY_ID=
AWS_DEFAULT_REGION=us-east-1
AWS_SECRET_ACCESS_KEY=
AWS_SESSION_TOKEN=
  • Lease an account
  • Create a CloudFormation stack with the TerminationProtection defined

## Testing CloudFormation Stack with Termination Protection
randomNum=$(cat /dev/urandom | LANG=c tr -dc '0-9' | head -c 12)
aws s3api create-bucket --bucket test-cf-termination-protection-$randomNum

cat << "EOF" > my-template.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Part 1 - Build a webapp stack with CloudFormation

Resources:
  WebAppInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0d5eff06f840b45e9 # ImageID valid only in us-east-1 region
      InstanceType: t2.micro
EOF

aws s3 cp my-template.yaml s3://test-cf-termination-protection-$randomNum/

# The stack has the termination protection enabled
aws cloudformation create-stack \
  --stack-name MyStack \
  --template-url https://s3.amazonaws.com/test-cf-termination-protection-$randomNum/my-template.yaml \
  --capabilities CAPABILITY_NAMED_IAM \
  --enable-termination-protection

aws cloudformation describe-stacks --stack-name MyStack
  • Clean the account by launching the package with the following config .vscode/launch.json config
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Package",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "main.go",
            "args": ["run", "-c", "./config.yml", "--no-dry-run"], // add "--no-dry-run" if needed
            "envFile": "${workspaceFolder}/.env",
            "console": "integratedTerminal"
        }
    ]
}

./config.yml (updating it for your account)

accounts:
  '<account_id>':
    filters:
      CloudFormationStack:
      - type: glob
        value: StackSet-AWSControlTowerBP-*
blocklist:
- '993122482293'
- '495548951594'
- 068377897844
regions:
- us-west-2
- us-east-1
- global
resource-types:
  includes:
  - CloudFormationStack
  - CloudFormationStackSet
  - CloudFormationType
settings:
  CloudFormationStack:
    DisableDeletionProtection: true

You should see an output similar to this:

aws-nuke - 3.0.0-dev - dirty
Do you really want to nuke the account with the ID 384736907310 and the alias 'alias-384736907310'?
Do you want to continue? Enter account alias to continue.
> alias-384736907310

starting scan for resources
us-west-2 - CloudFormationStack - StackSet-AWSControlTowerBP-BASELINE-CONFIG-7ef886d9-5049-44be-ac3c-142fbd665fa0 - [CreationTime: "2023-02-02T21:26:43Z", LastUpdatedTime: "2023-02-02T21:26:43Z", Name: "StackSet-AWSControlTowerBP-BASELINE-CONFIG-7ef886d9-5049-44be-ac3c-142fbd665fa0", Status: "CREATE_COMPLETE"] - filtered: filtered by config
us-west-2 - CloudFormationStack - StackSet-AWSControlTowerBP-BASELINE-CLOUDWATCH-312c6f12-9ef3-454c-8218-aab9727c996f - [CreationTime: "2023-02-02T21:26:43Z", LastUpdatedTime: "2023-02-02T21:26:43Z", Name: "StackSet-AWSControlTowerBP-BASELINE-CLOUDWATCH-312c6f12-9ef3-454c-8218-aab9727c996f", Status: "CREATE_COMPLETE"] - filtered: filtered by config
us-east-1 - CloudFormationStack - MyStack - [CreationTime: "2025-03-07T01:49:54Z", LastUpdatedTime: "2025-03-07T01:49:54Z", Name: "MyStack", Status: "CREATE_COMPLETE"] - would remove
us-east-1 - CloudFormationStack - StackSet-AWSControlTowerBP-BASELINE-CONFIG-1d65059d-b0d5-4321-a4b2-91d695332e86 - [CreationTime: "2023-02-02T21:26:42Z", LastUpdatedTime: "2023-02-02T21:26:42Z", Name: "StackSet-AWSControlTowerBP-BASELINE-CONFIG-1d65059d-b0d5-4321-a4b2-91d695332e86", Status: "CREATE_COMPLETE"] - filtered: filtered by config
us-east-1 - CloudFormationStack - StackSet-AWSControlTowerBP-BASELINE-CLOUDWATCH-3912dd49-778b-4723-8dfe-0c076a36c7ac - [CreationTime: "2023-02-02T21:26:42Z", LastUpdatedTime: "2023-02-02T21:26:42Z", Name: "StackSet-AWSControlTowerBP-BASELINE-CLOUDWATCH-3912dd49-778b-4723-8dfe-0c076a36c7ac", Status: "CREATE_COMPLETE"] - filtered: filtered by config
us-east-1 - CloudFormationStack - StackSet-AWSControlTowerBP-BASELINE-SERVICE-ROLES-42456b2f-fe1c-4af4-b40c-4d42941049d9 - [CreationTime: "2023-02-02T21:23:43Z", LastUpdatedTime: "2023-02-02T21:23:43Z", Name: "StackSet-AWSControlTowerBP-BASELINE-SERVICE-ROLES-42456b2f-fe1c-4af4-b40c-4d42941049d9", Status: "CREATE_COMPLETE"] - filtered: filtered by config
us-east-1 - CloudFormationStack - StackSet-AWSControlTowerBP-BASELINE-ROLES-0531f82a-0616-4c08-8191-8e1d90adc0cf - [CreationTime: "2023-02-02T21:23:43Z", LastUpdatedTime: "2023-02-02T21:23:43Z", Name: "StackSet-AWSControlTowerBP-BASELINE-ROLES-0531f82a-0616-4c08-8191-8e1d90adc0cf", Status: "CREATE_COMPLETE"] - filtered: filtered by config
Scan complete: 7 total, 1 nukeable, 6 filtered.

Do you really want to nuke the account with the ID 384736907310 and the alias 'alias-384736907310'?
Do you want to continue? Enter account alias to continue.
> alias-384736907310

ERRO[0062] CloudFormationStack stackName=MyStack attempt=0 maxAttempts=3 delete failed: ValidationError: Stack [MyStack] cannot be deleted while TerminationProtection is enabled
        status code: 400, request id: 3ebfba1e-32b4-45b9-b2a2-1d4e096a5937  component=scanner region=us-east-1 resource=CloudFormationStack
INFO[0067] CloudFormationStack stackName=MyStack attempt=0 maxAttempts=3 updating termination protection  component=scanner region=us-east-1 resource=CloudFormationStack
us-east-1 - CloudFormationStack - MyStack - [CreationTime: "2025-03-07T01:49:54Z", LastUpdatedTime: "2025-03-07T01:49:54Z", Name: "MyStack", Status: "CREATE_COMPLETE"] - triggered remove
Removal requested: 1 waiting, 0 failed, 6 skipped, 0 finished


us-east-1 - CloudFormationStack - MyStack - [CreationTime: "2025-03-07T01:49:54Z", LastUpdatedTime: "2025-03-07T01:49:54Z", Name: "MyStack", Status: "CREATE_COMPLETE"] - waiting for removal
Removal requested: 1 waiting, 0 failed, 6 skipped, 0 finished


us-east-1 - CloudFormationStack - MyStack - [CreationTime: "2025-03-07T01:49:54Z", LastUpdatedTime: "2025-03-07T01:49:54Z", Name: "MyStack", Status: "CREATE_COMPLETE"] - removed
Removal requested: 0 waiting, 0 failed, 6 skipped, 1 finished


Nuke complete: 0 failed, 6 skipped, 1 finished.

…s not have a roleARN

Signed-off-by: Gabriela S. Soria <[email protected]>
@gsoria gsoria changed the title fix(cloudformation-stack): fix nil pointer dereference when stack doe… fix(cloudformation-stack): fix nil pointer dereference when stack does not have a RoleARN Mar 7, 2025
@gsoria gsoria changed the title fix(cloudformation-stack): fix nil pointer dereference when stack does not have a RoleARN fix(cloudformation-stack): fix nil pointer dereference when stack does not have a RoleARN defined Mar 7, 2025
Copy link
Collaborator

@corybekk corybekk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Gaby! I was able to cleanup the cloudformation stack with deletion protection without issue.

waiting 3s before continuing
time="2025-03-08T00:29:40Z" level=error msg="CloudFormationStack stackName=MyStack attempt=0 maxAttempts=3 delete failed: ValidationError: Stack [MyStack] cannot be deleted while TerminationProtection is enabled\n\tstatus code: 400, request id: 03309d12-f626-42ab-a695-049e67e082dd" component=scanner region=us-east-1 resource=ComprehendEndpoint
time="2025-03-08T00:29:40Z" level=info msg="CloudFormationStack stackName=MyStack attempt=0 maxAttempts=3 updating termination protection" component=scanner region=us-east-1 resource=ComprehendEndpoint
us-east-1 - CloudFormationStack - MyStack - [CreationTime: "2025-03-08T00:06:19Z", LastUpdatedTime: "2025-03-08T00:06:19Z", Name: "MyStack", Status: "CREATE_COMPLETE"] - triggered remove
Removal requested: 1 waiting, 0 failed, 384 skipped, 0 finished


us-east-1 - CloudFormationStack - MyStack - [CreationTime: "2025-03-08T00:06:19Z", LastUpdatedTime: "2025-03-08T00:06:19Z", Name: "MyStack", Status: "CREATE_COMPLETE"] - waiting for removal
Removal requested: 1 waiting, 0 failed, 384 skipped, 0 finished


us-east-1 - CloudFormationStack - MyStack - [CreationTime: "2025-03-08T00:06:19Z", LastUpdatedTime: "2025-03-08T00:06:19Z", Name: "MyStack", Status: "CREATE_COMPLETE"] - removed
Removal requested: 0 waiting, 0 failed, 384 skipped, 1 finished


Nuke complete: 0 failed, 384 skipped, 1 finished.

@gsoria gsoria merged commit 7a55789 into oreilly-main Mar 11, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants