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 scriptsRun 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
- Finds the target. An id under
aws_resourcesin~/.grim/servers.jsonis a resource. Anything else must have a state directory under~/.grim/infra/, or the command stops withNo infra state directory for alias. - 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.--yesdoes not skip this. - 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.
- Makes you type the alias or id. Anything else ends with
nothing destroyed. Without a terminal it refuses unless--yesis given. - Empties the bucket, only for a bucket and only with
--force-empty, by runningaws s3 rm --recursiveon it. - Copies the state file into
snapshots/, then runsterraform destroy. - Tidies up. The state directory is moved to
~/.grim/infra/_archived/<id>-<timestamp>/and the entry is removed fromservers.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 --checkDNS 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.
Related spells
grim infra:list
Survey what runs on AWS
List every server and AWS resource grim has created, with a monthly cost estimate for each and the ids that infra:destroy takes.
grim infra:doctor
Check the way to AWS
Check that the aws CLI, terraform and working AWS credentials are in place, and print the setup steps for whatever is missing.
grim infra:dns
Point a domain at a server
Print the two DNS records that point a domain at a server, with step-by-step instructions for Cloudflare or Route 53, and check whether they are live yet.
grim infra:aws-ec2
Raise a server on AWS
Create one EC2 instance with a static address, a firewall and your SSH key in your AWS account, then hand it to grim server:setup.
grim infra:aws-s3
Conjure a private bucket
Create a private S3 bucket and an IAM user that can reach nothing else, then print the .env lines Laravel needs to use it.
grim server:remove-project
Tear a project down
Remove one environment of a project from its server, with its containers, volumes, database, vhost, certificate and directory.