# Stripping a Finalizer by Hand

Removing the finalizer yourself (`kubectl patch ... remove /metadata/finalizers`) skips the controller entirely: no destroy, no cleanup, no event. Sometimes that is the last resort, for example after a destroy that can never succeed and a manual cloud cleanup. Prefer the [abandon annotation](<https://captf.io/docs/concepts/deletion/held/#abandon>), which releases the same cases but runs the cleanup and records why. This page lists what a bare strip causes, so you can decide what to preserve first. The commands are in the [stuck destroy runbook](<https://captf.io/docs/operator-guide/runbooks/stuck-destroy/index.md>).

> [!CAUTION]
>
> **A bare strip leaves your cloud resources running and untracked**
>
> The state was the only record of them, and the state backups are collected with the object. Back up and clean up first; see “Before you strip” below.

## What happens when it is gone

The object is deleted as soon as the finalizer goes. Kubernetes then garbage-collects everything the object owns, and the controller can no longer act for it.

| What | Result |
| --- | --- |
| The cloud resources | **Keep running, untracked.** The state was the only record of them |
| The state Secrets | Collected if owned (owner reference). A chunk written since the last reconcile, or any chunk of an object paused since, has only the backend labels and is **left behind** |
| The state backups | Collected: they are owned by the object. The newest copy of a lost state goes with them |
| The durable inputs Secret | Collected, with the pinned image and the rendered module needed to destroy by hand |
| The plan key Secret, the Jobs and their pods | Collected |
| The state lock Lease, the run lease, the cluster write lease | **Left behind.** They carry no owner reference |
| The credential mirror | Collected once every owner is gone; the mirror’s owner list is not updated |
| The runner ServiceAccount and RoleBinding | Stay until the [sweep](<https://captf.io/docs/concepts/deletion/cleanup/#the-namespace-rbac-sweep>) finds the namespace empty |

Two of those bite later:

- **Untracked state.** A leftover state Secret has no owner and nothing that lists it. The state suffix is a hash of the namespace, kind and name, never the UID, so a new object with the same three derives the same suffix and reads that old state as its own.
- **Leftover leases.** A run or cluster write lease whose holder Job is gone is taken over by the next Job after the one-minute grace. The sweep deletes them once the namespace holds no `Terraform*` object.

## Before you strip

1. **Back up** the state Secrets, the backups and the durable inputs Secret, or remove their owner references so they survive: stuck destroy runbook, steps 2 and 3.
2. **Clean up the cloud resources.** Run the pinned image’s `destroy` against the backed-up state, or use the cloud console.
3. **Check `metadata.finalizers`.** Something else may have added one. Remove only CAPTF’s, by index.
4. **For a `TerraformCluster`, delete its machines first.** The destroy’s dependents wait is skipped when you strip, and a machine left behind loses its cluster.

> [!NOTE]
>
> **See also**
>
> - [Held deletions](<https://captf.io/docs/concepts/deletion/held/#abandon>).
> - [Cleanup and garbage collection](<https://captf.io/docs/concepts/deletion/cleanup/index.md>).
> - [Stuck Destroy](<https://captf.io/docs/operator-guide/runbooks/stuck-destroy/index.md>).
