Goal
Move an instance, volume, VCN, bucket or whole compartment to a new compartment without locking anyone out or losing alerts.
The move itself is simple. What catches teams out is that the new compartment's policies apply immediately, and the things that refer to the old compartment don't update themselves.
What actually changes
| Item | After the move | Action |
|---|---|---|
| Access policies | Destination policies apply immediately | Grant access in the destination first |
| Instance boot volume and VNICs | Not moved with the instance | Move the boot volume separately if needed |
| Block Volume associated resources | Not moved | Move volumes, backups and groups separately |
| VCN VNICs, private and ephemeral IPs | Move with the VCN, asynchronously. Routing doesn't change | Wait, then verify |
| Subnets, route tables, security lists, NSGs | Each has its own move command | Check each one's compartment after the move |
| Tags and tag defaults | Not updated | Retag if your tags reflect the compartment |
| Alarms | Still reference the old compartment | Update the metric compartment |
| Quotas (compartment moves) | Not checked or enforced in the destination | Check usage against quotas yourself |
| Security zones | Some moves out of a zone are blocked | Plan the move inside the same zone |
Oracle says most resources can be moved, but a few can't. Check the service's documentation for your resource type.
Caution
A move can remove access and grant it at the same moment. Workloads can get
NotAuthorizedOrNotFoundright after the move, and people in the destination can gain access to data they could not see before. Review both directions before moving anything that holds sensitive data.
Before the move
Set the variables once:
SRC=ocid1.compartment.oc1..source
DST=ocid1.compartment.oc1..destination
TENANCY=ocid1.tenancy.oc1..xxxx
INSTANCE=ocid1.instance.oc1..xxxx
Inventory
- List what's in the source compartment, in each region you use:
oci search resource structured-search \
--query-text "query all resources where compartmentId = '$SRC' && lifecycleState != 'TERMINATED' && lifecycleState != 'DELETED'" \
--query 'data.items[].{type:"resource-type", name:"display-name", state:"lifecycle-state"}' \
--output table
- List what's attached to each instance you plan to move:
oci compute volume-attachment list -c $SRC --instance-id $INSTANCE \
--query 'data[].{volume:"volume-id", state:"lifecycle-state"}' --output table
oci compute boot-volume-attachment list -c $SRC --instance-id $INSTANCE \
--availability-domain <ad-name> \
--query 'data[].{bootVolume:"boot-volume-id", state:"lifecycle-state"}' --output table
Access
- Compare policies on the source, the destination and their parents:
for COMP in $SRC $DST; do
echo "== $COMP"
oci iam policy list -c $COMP --all --query 'data[].{name:name, statements:statements}'
done
- Confirm that the groups running the workload have access in the destination.
- Find dynamic groups whose rules name the source compartment:
oci iam dynamic-group list -c $TENANCY --all \
--query "data[?contains(\"matching-rule\", '$SRC')].{name:name, rule:\"matching-rule\"}"
A rule such as instance.compartment.id = '<source-ocid>' no longer matches an instance after it leaves that compartment. Its instance principal calls then fail. Update the rule, or add the new compartment with Any {instance.compartment.id = '<ocid1>', instance.compartment.id = '<ocid2>'}. For non-default identity domains, also check the domain's dynamic groups in the Console.
- Check policies with conditions on compartment, including
target.resource.compartment.idor tag-based conditions. - Review pre-authenticated requests on buckets you plan to move. A request stops working if its creator loses the required permissions. Permissions are checked on each use, so a creator with access to only the old compartment breaks the link.
Monitoring and automation
- Find alarms that evaluate metrics in the source compartment:
oci monitoring alarm list -c $SRC --all \
--query 'data[].{name:"display-name", id:id, metricCompartment:"metric-compartment-id", query:query}'
- List Events rules in the source and its parents. A rule only sees its own compartment and children, so it may stop matching a moved resource. See OCI Events rule not firing.
oci events rule list -c $SRC --all --query 'data[].{name:"display-name", condition:condition}'
- Search Terraform, Resource Manager stacks, pipelines and scripts for the source OCID.
- Check budgets, cost-tracking tags and cost reports that group spend by compartment.
- Try the move on a non-production resource of the same type.
Warning
Update Terraform before its next apply. If the code still has the old
compartment_id, an apply can try to reverse your move.
Do the move
Compute and storage
oci compute instance change-compartment --instance-id $INSTANCE --compartment-id $DST
oci bv boot-volume change-compartment --boot-volume-id <boot-volume-ocid> --compartment-id $DST
oci bv volume change-compartment --volume-id <volume-ocid> --compartment-id $DST
Oracle's move pages don't list state requirements for these operations. Test with a running instance and an attached volume in non-production before you schedule production.
Network and load balancers
oci network vcn change-compartment --vcn-id <vcn-ocid> --compartment-id $DST
oci network subnet change-compartment --subnet-id <subnet-ocid> --compartment-id $DST
oci network route-table change-compartment --rt-id <route-table-ocid> --compartment-id $DST
oci network security-list change-compartment --security-list-id <list-ocid> --compartment-id $DST
oci network nsg change-compartment --nsg-id <nsg-ocid> --compartment-id $DST
oci lb load-balancer change-compartment --load-balancer-id <lb-ocid> --compartment-id $DST
The VCN moves immediately. Its attached resources move asynchronously and don't appear in the new compartment until that finishes.
Buckets
oci os bucket update --bucket-name my-bucket --compartment-id $DST
Several resources at once
bulk-move-resources is CLI and API only. All resources must start in the same compartment, you need move permission in both compartments, and it only runs from the home region:
oci iam compartment bulk-move-resources --generate-param-json-input resources > resources.json
# Edit resources.json
oci iam compartment bulk-move-resources --region <home-region> \
--compartment-id $SRC --target-compartment-id $DST \
--resources file://resources.json
A whole compartment
Moving a compartment takes all of its child compartments and resources with it:
oci iam compartment move --region <home-region> \
--compartment-id <compartment-ocid> --target-compartment-id <new-parent-ocid>
Before you run it, check these points:
- You have
manage all-resourceson the lowest shared parent of the current and destination compartments. - The destination has no compartment with the same name.
- The compartment is not a security zone compartment. Those can't be moved.
- You've reviewed inherited access. The new parent's policies take effect and the old parent's no longer apply.
- You've found policies that name the moved compartment by path. Policies on a shared ancestor update automatically, but other statements need a manual fix.
- You've checked destination quotas. A move that breaks a quota isn't blocked.
- You've planned the tags. Tag defaults from the old parent stay on existing resources, and the new parent's defaults must be added manually.
After the move
- Confirm each resource's compartment:
oci compute instance get --instance-id $INSTANCE --query 'data."compartment-id"' --raw-output
- Run an application smoke test, including any calls that use an instance principal.
- Point alarms at the new compartment and confirm that they evaluate real data:
oci monitoring alarm update --alarm-id <alarm-ocid> --metric-compartment-id $DST --force
- Fix tags and confirm that tag-based policies still match.
- Check Events rules with a real test event.
- Run
terraform planfor every stack that touches the resources. - Confirm that backups and scheduled jobs still run on their next cycle.
- Remove temporary policy statements and old dynamic group entries once the workload is stable.
Roll back
Moving a resource back uses the same command with the source OCID as the destination. If workloads lose access, adding a policy statement in the destination is often quicker.
Prevent it next time
- Build dynamic groups and policies around stable tags, where the design allows it, rather than a single compartment OCID.
- Keep compartment OCIDs in one Terraform variable or config file, not scattered through scripts.
- Add an Events rule that alerts on
changeinstancecompartmentandchangevolumecompartmentevents, so production moves aren't a surprise.