Skip to content

Unmake what grim made

$ grim infra:destroy vps1

Everything the infra:aws-* wizards create is billed until it is gone, and deleting a server by hand in the AWS console leaves its address, firewall and key behind. grim infra:destroy removes a whole stack the way it was made, through Terraform, and then clears grim's record of it. It is also how you clean up after a wizard that failed halfway.

One command serves both kinds of thing: a server by its alias, or a bucket, trail, alarm set or budget by its resource id. grim infra:list shows both.

Usage

$ grim infra:destroy vps1                                   # a server and everything made with it
$ grim infra:destroy alarms-i-0abc1234def567890             # an AWS resource, by id
$ grim infra:destroy s3-shop-staging --force-empty          # a bucket that still holds files
$ grim infra:destroy vps1 --yes                             # no typed confirmation, for scripts

Run it from anywhere. It needs the aws CLI, terraform, and the state directory ~/.grim/infra/<id>/ the wizard left behind. The AWS profile saved at creation is used unless you pass --profile.

Arguments and options

Tear down a grim-managed AWS stack or resource (with multi-step confirmation)

Usage

grim infra:destroy [--profile PROFILE] [--force-empty] [--yes] [--] <alias>

Arguments

Argument Description
alias Server alias (from infra:aws-ec2) or AWS resource ID (from infra:aws-s3 / -cloudtrail / -alarms / -budget) (required)

Options

Option Description
--profile=PROFILE AWS CLI profile to use
--force-empty For S3 buckets: empty the bucket before destroy (otherwise terraform refuses)
--yes Skip the typed-alias confirmation (still prints the resource list)

What it actually does

  1. Finds the target. An id under aws_resources in ~/.grim/servers.json is a resource. Anything else must have a state directory under ~/.grim/infra/, or the command stops with No infra state directory for alias.
  2. Checks the account. It asks AWS who you are and compares the answer with the account recorded at creation. On a difference it prints Account mismatch — refusing to destroy. with both account numbers and stops. --yes does not skip this.
  3. Prints what is about to go. For a server: alias, account, profile, region, instance and the public IP that will be released, then every project registered on that host. For a resource: id, type, account, region and one line on what you lose.
  4. Makes you type the alias or id. Anything else ends with nothing destroyed. Without a terminal it refuses unless --yes is given.
  5. Empties the bucket, only for a bucket and only with --force-empty, by running aws s3 rm --recursive on it.
  6. Copies the state file into snapshots/, then runs terraform destroy.
  7. Tidies up. The state directory is moved to ~/.grim/infra/_archived/<id>-<timestamp>/ and the entry is removed from servers.json.

If terraform destroy fails, the command stops after step 6 with Terraform's error. The state directory and the record stay where they are, so you can fix the cause and run it again.

What is removed, by type

Target Removed in AWS Left behind
Server The instance and its disk, the Elastic IP, the security group, the key pair Projects registered on the host stay in servers.json, pointing at nothing
Bucket s3-… The bucket and its settings, the IAM user, its policy and its access key Nothing. Apps still using that key start getting 403
Trail cloudtrail-… The trail, and with security-focused the log group and its role The log bucket, as long as it holds logs
Alarms alarms-… Everything that run created: the two alarms, the email subscription and the grim-alarms topic The instance, unwatched
Budget budget-… The budget and its notifications Nothing

There is no undo in AWS. A destroyed server takes its disk with it: the database, the uploads and every .env on it. A destroyed bucket takes its files. The archived directory holds Terraform's files and your generated SSH key, not your data. Pull a dump and the .env files before you type the alias.

Pitfalls

The bucket is not empty

terraform destroy failed: with BucketNotEmpty in it is followed by Re-run with --force-empty to purge bucket contents first. Do that once you are sure nothing in the bucket is needed. --force-empty deletes the current objects. A bucket with versioning on, which is how grim infra:aws-s3 makes them, also keeps earlier versions that this does not remove; if the error comes back, empty the bucket in the AWS console, where the button removes versions too, and run the destroy again.

The trail's log bucket blocks the destroy

--force-empty applies to buckets made by grim infra:aws-s3 only. For a trail, the trail itself is removed and recording stops, then Terraform fails on the log bucket because audit logs are in it, and the entry stays in grim infra:list. That is deliberate protection of the history. Keep the bucket and leave the entry, or empty the bucket in the console and run the destroy again.

A failed wizard left no record

No servers.json entry for "vps1" — treating as a partial-apply cleanup. comes up when a wizard died between creating things and writing its record. grim then cannot know the profile: --profile is required to clean up a partial-failure state dir. Look in ~/.grim/infra/vps1/terraform.tfvars for the region, pass --profile, and check the account it prints before you confirm, because nothing is on record to compare it with.

The projects on the server are not unregistered

After a server is gone its projects are still in servers.json under the dead address, and the command says so. Point each at a new server with grim server:add-project, or drop it with grim server:remove-project.

After it finishes

Destroyed. and the path of the archived state. Billing for those resources stops with them. Check that nothing else is left, and delete the archive when you no longer need the generated key or the old variables; grim never cleans _archived/ itself.

$ grim infra:list --check

DNS records that pointed at the released address are yours to remove at your DNS provider. Until you do, the domain points at an address AWS may hand to someone else.