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 destroyor 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.