Arness Infra Cleanup
Check for expired ephemeral infrastructure resources across three TTL sources (issue tracker, TTL registry, resource manifest) and destroy them with user confirmation. Supports periodic monitoring via /loop for session-duration TTL enforcement.
This skill never auto-destroys resources. Every destruction requires explicit user confirmation with a summary of what will be destroyed and its associated cost.
Prerequisites
Read ## Arness from the project's CLAUDE.md. If no ## Arness section exists or Arness Infra fields are missing, inform the user: "Arness Infra is not configured for this project yet. Run /arn-infra-wizard to get started — it will set everything up automatically." Do not proceed without it.
Check the Deferred field. If Deferred: yes, inform the user: "Infrastructure is in deferred mode. Cleanup is not available until infrastructure is fully configured. Run /arn-infra-assess to un-defer." Stop.
Extract:
- Experience level -- derived from user profile. Read
~/.arness/user-profile.yaml(or.claude/arness-profile.local.mdif it exists — project override takes precedence). Apply the experience derivation mapping from${CLAUDE_PLUGIN_ROOT}/skills/arn-infra-ensure-config/references/experience-derivation.md. If no profile exists, check for legacyExperience levelin## Arnessas fallback. - Providers -- which cloud providers are configured
- Providers config -- path to
providers.md - Default IaC tool -- for determining destroy commands
- Environments -- environment names in promotion order
- Environments config -- path to
environments.mdfor per-environment deployment state - Resource manifest -- path to
active-resources.jsonfor resource inventory - Reference overrides -- path to local reference override directory (for evolving reference reads)
- Platform -- for issue tracker access
- Issue tracker -- for reading cleanup issues and updating labels
- Tooling manifest -- path to
tooling-manifest.jsonfor available CLIs
Workflow
Step 1: Scan TTL Sources
Check all three TTL sources in priority order. Collect a unified list of resources with expired or near-expiry TTLs.
Source 1: Issue Tracker (highest priority)
If issue tracking is configured (Platform is github or Issue tracker is jira):
For GitHub:
gh issue list --label "arn-infra-cleanup" --state open --json number,title,body,createdAt
For Jira: Query issues with label arn-infra-cleanup in the configured Jira project.
Parse each cleanup issue:
- Extract TTL expiry timestamp from the issue body (set by
arn-infra-deployduring ephemeral deployments) - Extract resource identifiers and destroy commands from the issue body
- Determine if the TTL has expired (expiry timestamp < current time)
- Determine if the TTL is approaching (within 1 hour of expiry) for early warning
Source 2: TTL Registry (secondary)
Check for the TTL registry file:
Read .arness/infra/ttl-registry.md
If the file exists, parse resource entries with their TTL timestamps. The TTL registry is a fallback for environments without issue tracking or for resources created outside the normal deployment flow.
Expected format:
# TTL Registry
| Resource | Environment | Provider | Created | TTL Expiry | Destroy Command |
|----------|-------------|----------|---------|------------|-----------------|
| [name] | [env] | [provider] | [timestamp] | [expiry] | [command] |
Source 3: Resource Manifest (tertiary)
Read the resource manifest:
Read <resource-manifest-path>
Filter resources where:
ttlis not nullttltimestamp is in the past (expired)statusisactive(not already destroyed)
Step 2: Compile Expired Resource List
Merge results from all three sources, deduplicating by resource ID. For each expired resource, compile:
- Resource name and type
- Provider and region
- Environment
- Created timestamp
- TTL expiry timestamp (how long ago it expired)
- Estimated cost since creation (calculate:
monthly_cost × (hours_since_creation / 730)) - Destroy command (IaC-tool-appropriate)
- Source (which TTL source identified this resource)
If no expired resources are found:
Present: "No expired resources found. All TTLs are current."
If there are resources with TTLs approaching expiry (within 1 hour), list them as early warnings: "The following resources will expire soon:
- [resource]: expires in [time remaining]"
Stop here (or if running via /loop, wait for the next interval).
If expired resources are found:
Continue to Step 3.
Step 3: Present Expired Resources for Confirmation
Present each expired resource group (grouped by environment):
"The following resources have expired TTLs and are candidates for cleanup:
Environment: [env]
| Resource | Type | Provider | Expired | Cost Since Creation | Destroy Command | |----------|------|----------|---------|--------------------| --------------- | | [name] | [type] | [provider] | [time since expiry] | $[amount] | [command] |
Total cost of expired resources: $[total] (estimated charges since TTL expired)
Ask (using AskUserQuestion):
"How would you like to handle the expired resources?"
Options:
- Destroy all -- Destroy all expired resources listed above
- Select -- Choose which resources to destroy
- Extend TTL -- Extend the TTL for some or all resources
- Skip -- Do not destroy anything right now
NEVER auto-destroy without confirmation. Even when running via /loop, each cleanup cycle must ask for confirmation before destroying anything.
Step 4: Execute Destruction
For each confirmed resource, execute the appropriate destroy command.
Read the local override or plugin default for rollback-patterns.md.
Destroy commands by IaC tool:
| IaC Tool | Destroy Command | |----------|----------------| | OpenTofu | tofu destroy -target=<resource> -var-file=environments/<env>.tfvars | | Terraform | terraform destroy -target=<resource> -var-file=environments/<env>.tfvars | | Pulumi | pulumi destroy --target <urn> --stack <env> --yes | | CDK | cdk destroy --context env=<env> | | Bicep | az resource delete --ids <resource-id> | | kubectl | kubectl delete <resource-type> <name> -n <namespace> | | Helm | helm uninstall <release> -n <namespace> | | Fly.io | fly apps destroy <app-name> --yes | | Railway | railway down or delete via dashboard | | Vercel | vercel remove <project> --yes | | Netlify | netlify sites:delete --id <site-id> | | Render | Delete via Render dashboard |
For targeted IaC destruction (OpenTofu/Terraform):
When destroying specific resources from a larger state file, use -target to avoid destroying the entire environment:
tofu destroy -target=module.<module>.<resource_type>.<name> -var-file=environments/<env>.tfvars
For full environment teardown: If all resources in an environment are expired, offer full environment destruction:
tofu destroy -var-file=environments/<env>.tfvars
Important: Always ask for confirmation before executing destroy commands. Show the exact command that will be run.
Step 5: Update Tracking
After successful destruction:
5a. Update resource manifest:
- Set
statustodestroyedfor each destroyed resource - Update
lastUpdatedtimestamp - Recalculate the summary section
5b. Close/update cleanup issues: If the resource was tracked via an issue:
- For GitHub: close the issue with a comment "Resources destroyed by arn-infra-cleanup at [timestamp]"
``bash gh issue close <number> --comment "Resources destroyed by arn-infra-cleanup at [timestamp]." ``
- For Jira: transition the issue to Done/Closed
5c. Update TTL registry: If the resource was tracked in .arness/infra/ttl-registry.md, remove the entry or mark it as destroyed.
5d. Update Environments config: If all resources in a