Python Instead of the OCI Console
OCI is Oracle Cloud Infrastructure, which is Oracle’s cloud. Roughly the same idea as AWS or GCP, with different names for everything.
Most of my infra work on it started as clicking around in the console until something worked. That is fine once. It is not fine the fourth time, when you cannot remember which of the six things you changed was the one that mattered.
So now I write the boring parts in Python. The console and the CLI are both fine, but Python is where I can keep the inventory, the guardrails, and a summary I can actually read afterwards.
The loop I follow is pretty simple:
- Ask what is true right now.
- Plan exactly what I am about to touch.
- Apply the smallest change I can.
- Verify that reality actually changed.
- Print the results so I have receipts.
Doing it this way means you do not have to remember anything. The script remembers for you.
Locally I just reuse the config the OCI CLI already uses, which lives at ~/.oci/config.
import ocicfg = oci.config.from_file()When the code is running inside OCI itself, on a compute instance, I switch to instance principals so I am not shipping my own keys around.
import ocisigner = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()compute = oci.core.ComputeClient({}, signer=signer)A script I actually keep around
Section titled “A script I actually keep around”This is what I mean by controlling infra with Python. It is not a framework. It is a small tool that lists instances in a compartment, filters them by tag, prints what it is about to do, and then stops them if I let it.
import oci
cfg = oci.config.from_file()cpt = "ocid1.compartment.oc1..REDACTED"
compute = oci.core.ComputeClient(cfg)inst = oci.pagination.list_call_get_all_results( compute.list_instances, compartment_id=cpt).data
targets = [i for i in inst if (i.freeform_tags or {}).get("env") == "staging"][:5]print("PLAN:", [t.display_name for t in targets])
DRY = Trueif not DRY: for t in targets: compute.instance_action(t.id, "STOP") print("STOP requested:", t.display_name)Three things are doing the real work here. The selection is explicit, the limit is enforced by that
[:5], and dry run is the default. You have to go out of your way to actually break something.
Guardrails
Section titled “Guardrails”Print what you are about to touch before you touch it. Reading a plan takes five seconds and has saved me more than once.
Give every script a brake pedal. A hard limit on how many things it can affect means a bad filter costs you five instances instead of fifty.
Target by tags and compartments, never by whatever happens to match a name. Names are not a safety mechanism.
Verify afterwards. An API call returning successfully is not the same as the system being in the state you wanted.
Print a summary, not 400 lines. I want to see that 5 succeeded and 1 failed, with names.
Why this works for me
Section titled “Why this works for me”OCI itself is not the hard part. The hard part is doing the same thing repeatedly over months without slowly inventing new mistakes.
Python does not make any of this clever. It just makes the steps I already trust happen the same way every time, which turns out to be the thing I actually wanted.