Home / OCI

OCITroubleshootingIntermediate

OCI compartment won't delete: find the leftover resources in every region and clean up safely

A compartment delete goes to Deleting and then back to Active. Find what is still inside it in every subscribed region, including policies, child compartments and vaults, and clean up or move it safely.

Deepak Bhardwaj6 min read

Applies toOCI IAM compartments, OCI Search (Resource Query Language), Tenancy Explorer, OCI CLI 3.x

Symptom

You delete a project or test compartment. The Console shows Deleting for a few minutes, then the compartment goes back to Active. If you used the CLI, the work request ends in a failed state.

The rule is simple: a compartment can only be deleted when it is empty. That includes policies attached to it and child compartments. The hard part is finding what is left, because:

  • Compartments are global, but most resources are regional. A forgotten bucket in another subscribed region blocks the delete.
  • OCI Search does not return IAM policies.
  • You only see resources you have permission to read. Someone else's resources can be invisible to you.

Check the delete work request first

The delete is asynchronous. Capture the work request ID so you can see how it ended:

C=ocid1.compartment.oc1..xxxx

oci iam compartment delete --compartment-id $C --force
# The response includes "opc-work-request-id"

oci iam work-request get --work-request-id <opc-work-request-id> \
  --query 'data.{status:status, op:"operation-type", started:"time-started", finished:"time-finished"}' \
  --output table

If the status is FAILED, the compartment is not empty. Run IAM operations like this one from the home region (--region <home-region>).

Find leftovers in every subscribed region

1. Search each region

Resource Query Language searches are regional, so loop over your subscribed regions:

C=ocid1.compartment.oc1..xxxx

for R in $(oci iam region-subscription list --query 'data[]."region-name"' --raw-output | jq -r '.[]'); do
  echo "== $R"
  oci search resource structured-search --region "$R" \
    --query-text "query all resources where compartmentId = '$C' && lifecycleState != 'TERMINATED' && lifecycleState != 'DELETED'" \
    --query 'data.items[].{type:"resource-type", name:"display-name", state:"lifecycle-state"}' \
    --output table
done

The result is the list to work through. Pay attention to states like PENDING_DELETION, STOPPED and AVAILABLE. They are still resources.

2. Policies and child compartments

Search does not return policies, so list them directly. Also check for child compartments, which must be deleted (or moved) first:

oci iam policy list --compartment-id $C --all \
  --query 'data[].{name:name, id:id}' --output table

oci iam compartment list --compartment-id $C --lifecycle-state ACTIVE --all \
  --query 'data[].{name:name, id:id}' --output table

3. Things you can't see

If search returns nothing and the delete still fails, you are probably missing read permissions on something. Repeat the checks as a tenancy administrator, or use Governance & Administration → Tenancy Management → Tenancy Explorer with an admin account. Tenancy Explorer only shows supported resource types, so use it together with the CLI checks.

Common blockers

What you find Why it blocks Fix
Policy attached to the compartment Policies are resources in the compartment Move the statements to a parent compartment's policy if still needed, then delete the policy
Child compartment A parent can't be deleted before its children Delete the child first (same checks), or move it with oci iam compartment move
Bucket with objects A bucket can't be deleted while it has objects or object versions Empty it, including old versions if versioning was on, then delete it
Bucket with a locked retention rule Objects can't be deleted until retention expires Move the bucket to another compartment instead
Vault or key in PENDING_DELETION It still exists until the waiting period ends Wait for the deletion date, or move the vault before you schedule deletion
Boot volumes, volume backups, custom images Detached or preserved resources remain after instances are terminated Confirm nobody needs them, then delete or move them
Resources in another region The search was only run in the home region Rerun the loop above across all subscribed regions
Identity domain (non-default) Domains live in a compartment Deactivate and delete the domain, or move it

Caution

Deleting a policy, vault, key or backup can cut off access or make encrypted data unrecoverable. Before you delete anything, check that no resource in another compartment depends on it. A volume in production can use a key from a vault in the "old" compartment. See Vault master key disabled or scheduled for deletion.

Clean up

Delete in bulk where it's safe

For many small resources in one compartment, the bulk delete API saves time. It runs from the home region and creates a work request:

oci iam compartment bulk-delete-resources --generate-param-json-input resources > resources.json
# Edit resources.json: one entry per resource to delete

oci iam compartment bulk-delete-resources --compartment-id $C --resources file://resources.json

Not every resource type is supported, and some types need extra identifying fields. Run oci iam compartment bulk-delete-resources --help to check your CLI version's options.

Move what must be kept

If something must be kept, such as audit evidence, a vault, or a bucket under retention, move it to an archive compartment instead of deleting it. Most resources have a change-compartment command, for example:

oci os bucket update --bucket-name audit-exports --compartment-id ocid1.compartment.oc1..archive
oci kms management vault change-compartment --vault-id ocid1.vault.oc1..xxxx \
  --compartment-id ocid1.compartment.oc1..archive

Warning

A moved resource follows the policies of its new compartment straight away. Groups that had access through the old compartment lose it, and groups with access to the target gain it. Check the target compartment's policies before you move anything sensitive.

For several resources, oci iam compartment bulk-move-resources works the same way as the bulk delete.

Delete the compartment

Rerun the search loop and the policy and child checks. When all three return nothing, delete the compartment again:

oci iam compartment delete --compartment-id $C --force
oci iam compartment get --compartment-id $C --query 'data."lifecycle-state"' --raw-output

After a successful delete, the state becomes DELETED and OCI appends a random suffix to the name, so you can reuse the original name. The deleted compartment stays visible on the Compartments page for 90 days. Policy statements that referenced it are updated to the new name. Remove those statements so they don't confuse the next person who reads them.

Prevent it next time

  • Tag every compartment with an owner and an end date (a defined tag such as Operations.Owner), so leftovers have an owner to ask.
  • Tear down project environments with the same tool that built them. A terraform destroy or Resource Manager destroy job removes what it created. Console-built additions still need the search above.
  • Keep shared resources, such as vaults, keys, DNS zones and logging buckets, in a long-lived shared compartment, not in project compartments.
  • Add the multi-region search loop to your decommissioning checklist and save its output as evidence before you delete the compartment.
  • Use compartment quotas to keep test compartments from creating resource types you can't easily clean up.