DISCLAIMER - This is not an official Oracle application. It is not supported by Oracle Support.
This repository provides ./oci-tenancy-review, a CLI tool to easily generate OCI tenancy bill of materials (BOMs) exported as CSV, compatible with Cloud Shell.
We focus on speed by concurrently scraping specific OCI domains (e.g. compute, block-storage, base-database, object-storage, limits) rather than providing a full view of a whole OCI tenancy. These CSV artifacts can be cached granularly, making the process resumable.
Here's a sample video on how to quickly download a full archive of usage CSVs of your tenancy via Cloud Shell (fallback video link):
oci-tenancy-review-own-720p-web.mp4
- OCI Tenancy Review
- Prerequisites
- Setup
- Usage
- Exported Files
report/regions.txtreport/compartments.csvreport/policies/policy_statements.csvreport/compute/compute_instances.csvreport/compute/compute_shapes_summary.csvreport/storage/storage_inventory.csvreport/object-storage/buckets_inventory.csvreport/base-database/base_databases.csvreport/limits/service_limits.csvreport/limits/compute_limits.csvreport/limits/block_storage_limits.csvreport/limits/object_storage_limits.csv- Per-Region Reports
- Tests
- Safety & Auditability
- Design Choices
- Alternatives
If running in OCI Cloud Shell all tooling is already available.
For local runs, ensure the following is installed and configured:
- jq
- OCI CLI
- UNIX tooling like
bash,awk,sort - (Optional, advanced) GNU
makefor parallel fan-out and cached top-level CSV targets
git clone https://github.com/majodev/oci-tenancy-review.git
cd oci-tenancy-review
chmod +x ./oci-tenancy-review
You need administrative access to your tenancy, the tenancy you want to inspect must be supplied via the env var TENANCY_OCID.
Use one of the following three options depending on your environment:
Option 1: Automatically set TENANCY_OCID within OCI Cloud Shell
If you are running inside Cloud Shell, simply reuse the tenancy OCID from current OCI CLI context:
export TENANCY_OCID="${TENANCY_OCID:-$(
oci iam compartment list \
--include-root \
--compartment-id-in-subtree true \
--access-level ANY \
--all \
--query 'data[?starts_with(id, `ocid1.tenancy.`)].id | [0]' \
--raw-output
)}"
echo "Your tenancy OCID is: '${TENANCY_OCID}'"
Option 2: Automatically set TENANCY_OCID on your local machine
Read tenancy OCID from ~/.oci/config (uses OCI_CLI_PROFILE or DEFAULT):
OCI_PROFILE="${OCI_CLI_PROFILE:-DEFAULT}"
export TENANCY_OCID="${TENANCY_OCID:-$(
awk -v profile="$OCI_PROFILE" '
$0=="[" profile "]" {in_profile=1; next}
/^\[/ {in_profile=0}
in_profile && $1 ~ /^tenancy[[:space:]]*=/ {
sub(/^[^=]*=[[:space:]]*/, "", $0); print $0; exit
}
' ~/.oci/config
)}"
echo "Your tenancy OCID is: '${TENANCY_OCID}'"
Option 3: Set TENANCY_OCID manually
To explicitly set your TENANCY_OCID use the following:
# cat ~/.oci/config
export TENANCY_OCID="ocid1.tenancy.oc1...."
echo "Your tenancy OCID is: '${TENANCY_OCID}'"
# Run all reports (delegates to `make -j 4 --no-print-directory all`, so 4 jobs can execute concurrently)
./oci-tenancy-review all
# Your reports are now available in the report subfolder
# If you are within an OCI Cloud Shell, you may want to archive this folder to download it more easily
tar -czvf report.tar.gz report
If you used OCI Cloud Shell to execute the above, you should now be able to download the archived report by navigating to "Cog -> Download" (top right) and targeting this file:
oci-tenancy-review/report.tar.gz
Now inspect all .csv files in that archive.
For larger tenancies or repeated runs, use make to execute region/compartment fan-out concurrently and cache top-level CSV outputs.
# Build all top-level CSVs (cached by file timestamps) with 4 job runners
make -j 4 --no-print-directory all
# Run specific job runner concurrently
make -j 4 --no-print-directory regions compartments policies compute block-storage base-database object-storage limits
# Build a specific CSV artifact (this will execute the dependent runner)
make -j 4 --no-print-directory report/compute/compute_instances.csv
make -j 4 --no-print-directory report/limits/service_limits.csvIf REGIONS is unset, discovery runs in all subscribed regions (the default case).
Set REGIONS to override this and target one or more specific regions:
# single region
export REGIONS="eu-frankfurt-1"
# multiple regions (comma-separated)
export REGIONS="eu-frankfurt-1,eu-zurich-1"When targeting regions (default all subscribed or REGIONS), the script
first checks if each region is reachable and automatically skips unreachable ones.
By default, eu-kragujevac-1 is blacklisted. You can override blacklist regions with:
export BLACKLISTED_REGIONS="eu-amsterdam-1"Note that the following runs uncached and serial. Use make -j 4 --no-print-directory <cmd> if you want to control job concurrency and have artifact caching.
# Build report/regions.txt (reachable, non-blacklisted target regions)
./oci-tenancy-review regions
# Build report/compartments.csv
./oci-tenancy-review compartments
# Build compute inventory CSVs at report/compute/
./oci-tenancy-review compute
# Build compute inventory for one region at report/compute/regions/<region>/
./oci-tenancy-review compute-region eu-frankfurt-1
# Build block + boot volume inventory CSV at report/storage/
./oci-tenancy-review block-storage
# Build storage inventory for one region at report/storage/regions/<region>/
./oci-tenancy-review block-storage-region eu-frankfurt-1
# Build object storage bucket inventory CSV at report/object-storage/
./oci-tenancy-review object-storage
# Build object storage inventory for one region at report/object-storage/regions/<region>/
./oci-tenancy-review object-storage-region eu-frankfurt-1
# Build base database inventory CSV at report/base-database/
./oci-tenancy-review base-database
# Build base database inventory for one region at report/base-database/regions/<region>/
./oci-tenancy-review base-database-region eu-frankfurt-1
# Build compute + block-storage + object-storage limits posture CSV at report/limits/
./oci-tenancy-review limits
# Build compute-only limits CSV at report/limits/compute_limits.csv
./oci-tenancy-review compute-limits
# Build block-storage-only limits CSV at report/limits/block_storage_limits.csv
./oci-tenancy-review block-storage-limits
# Build object-storage-only limits CSV at report/limits/object_storage_limits.csv
./oci-tenancy-review object-storage-limits
# Build limits posture for one region at report/limits/regions/<region>/
./oci-tenancy-review limits-region eu-frankfurt-1
# Build report/policies/policy_statements.csv
./oci-tenancy-review policies
# Enable debug shell tracing (-x)
DEBUG=true ./oci-tenancy-review computeNotes:
- All outputs are written relative to current working directory under
report/. - Run
./oci-tenancy-review helpfor command help.
Newline-separated target region names used for discovery (for example eu-frankfurt-1), after applying:
REGIONSselection (or all subscribed if unset)- reachability checks
BLACKLISTED_REGIONSexclusions
CSV header:
compartment-idcompartment-path
CSV header:
compartment-idcompartment-path(human-readable path,:separated)statement-seqpolicy-namepolicy-statement(one row per statement)created-by(from defined tagOracle-Tags.CreatedBywhen present)time-createdpolicy-lifecycle-statepolicy-descriptionpolicy-freeform-tag-countpolicy-defined-tag-namespace-countid(policy OCID)
CSV header:
compartment-idcompartment-pathregioninstance-nameshapeocpusmemory-in-gbsbaseline-ocpu-utilizationlifecycle-stateavailability-domainfault-domainimage-idlaunch-modelaunch-boot-volume-typelaunch-firmwarelaunch-remote-data-volume-typesource-typecapacity-reservation-iddedicated-vm-host-idtime-maintenance-reboot-duepreemptible-action-typeavailability-recovery-actionlegacy-imds-endpoints-disablednetwork-typeconsistent-volume-naming-enabledall-plugins-disabledmonitoring-disabledmanagement-disabledlive-migration-preferredpv-encryption-in-transit-enabledmetadata-key-countextended-metadata-key-countplatform-typeplatform-secure-boot-enabledplatform-tpm-enabledplatform-measured-boot-enabledplatform-memory-encryption-enabledfreeform-tag-countdefined-tag-namespace-counttime-createdinstance-id
CSV header:
shapecount
Shape distribution summary derived from compute_instances.csv.
CSV header:
compartment-idcompartment-pathregionkind(block-volumeorboot-volume)display-namelifecycle-stateavailability-domainsize-in-gbsvpus-per-gbis-hydratedauto-tune-enabledauto-tuned-vpus-per-gbautotune-policy-countvolume-group-idreplica-ad-listbackup-policy-idbackup-countlatest-backup-timelatest-backup-typereplica-countdr-protection(YES/NO)kms-key-idimage-id(boot volumes)freeform-tag-countdefined-tag-namespace-counttime-createdid
This report is designed for storage overview plus DR/failure-mode discovery (backup and replication coverage).
CSV header:
compartment-idcompartment-pathregionnamespacebucket-namebucket-created-bytime-createdetagfreeform-tag-countdefined-tag-namespace-countfreeform-tag-keys(;-joined key names)defined-tag-namespaces(;-joined namespace names)defined-tag-key-count(sum of tag keys across defined-tag namespaces)freeform-tags-json(raw freeform tags object)defined-tags-json(raw defined tags object)bucket-idpublic-access-typestorage-tierobject-events-enabledreplication-enabledis-read-onlyversioningauto-tieringkms-key-idapproximate-object-countapproximate-size-bytesobject-lifecycle-policy-etagmetadata-key-countid
Implementation detail: each bucket now uses oci os bucket get for enrichment, while Make fan-out remains region -> compartment for concurrency.
CSV header:
compartment-idcompartment-pathregiondb-namedb-unique-namedb-workloaddb-versiondb-system-iddb-home-idvm-cluster-idcdb-namepdb-namelifecycle-statelifecycle-detailscharacter-setncharacter-setis-cdbkms-key-idkms-key-version-idkey-store-idkey-store-wallet-namevault-iddatabase-software-image-idsid-prefixauto-backup-enabledbackup-recovery-window-in-daysbackup-destination-typeauto-full-backup-daylast-backup-timestamplast-failed-backup-timestamplast-backup-duration-in-secondsfreeform-tag-countdefined-tag-namespace-counttime-createdid
CSV header:
regionservice-namelimit-namescope-typeavailability-domainlimit-valueusedavailableusage-percentid(stable key:region:service-name:scope-type:availability-domain:limit-name)
Rows are ordered by usage-percent descending, then region/service/limit.
This report currently focuses on compute, block-storage, and object-storage service limits.
Same schema as service_limits.csv, scoped to service-name=compute.
Same schema as service_limits.csv, scoped to service-name=block-storage.
Same schema as service_limits.csv, scoped to service-name=object-storage.
When region workflows are used (for example via compute, block-storage, base-database, object-storage, limits), each domain
also writes per-region artifacts:
report/compute/regions/<region>/compute_instances.csvreport/storage/regions/<region>/storage_inventory.csvreport/base-database/regions/<region>/base_databases.csvreport/object-storage/regions/<region>/buckets_inventory.csvreport/limits/regions/<region>/service_limits.csv
Bats tests are under test/test_main.bats.
bats test/test_main.batsThe script is intentionally designed to be straightforward to audit:
- Read-only OCI usage: all OCI CLI interactions use read-oriented operations (for example
list,get,search). - Local write scope: generated artifacts are written under
./report/. - Runtime call log: OCI calls and exit codes are written to
report/run.logby default. Override withRUN_LOG_FILE.
In CI/CD, we enforce this read-only expectation with automated tests. The Bats test suite includes:
- a blacklist guard that scans executable source for mutating OCI CLI verbs (
create,update,delete,patch,put,remove,bulk-delete) and fails if any are introduced, and - a whitelist guard that allows only explicitly approved OCI invocation patterns used by this project.
The GitHub Actions workflow runs these tests on every push and pull request.
The following mapping summarizes top-level ./oci-tenancy-review commands and OCI CLI calls used by the script.
-
Shared calls:
oci iam region-subscription listoci iam availability-domain listoci iam compartment list
-
regions:oci iam region-subscription listoci iam availability-domain list
-
compartments:oci iam compartment list
-
policies:oci search resource structured-search(policy resources)oci iam policy list
-
compute,compute-region:oci search resource structured-search(instance resources)oci compute instance list
-
block-storage,block-storage-region:oci search resource structured-search(volume resources)oci search resource structured-search(bootvolume resources)oci bv volume listoci bv boot-volume listoci bv backup listoci bv boot-volume-backup listoci bv block-volume-replica listoci bv boot-volume-replica list
-
base-database,base-database-region:oci search resource structured-search(database resources)oci db database list
-
object-storage,object-storage-region:oci search resource structured-search(bucket resources)oci os ns getoci os bucket listoci os bucket get
-
compute-limits,block-storage-limits,object-storage-limits,limits,limits-region:oci limits value listoci limits resource-availability get
-
all:- Delegates to
make; resulting OCI calls are the union of the selected domain commands.
- Delegates to
This repository intentionally separates concerns:
./oci-tenancy-reviewis responsible for fetching OCI data and exporting report artifacts per domain (JSON/CSV).Makefileis responsible for orchestration concerns such as concurrent job execution and cache-aware target rebuilding.
In practice, the script defines what data is fetched and exported, while make defines how work is scheduled and reused.
This tool is intentionally specialized for fast, targeted tenancy BOM extraction. If your primary goal is broader compliance coverage, there are stronger alternatives:
| Alternative | Primary Goal | Collection Model | Typical Scope | Runtime/Complexity Profile | Best Fit |
|---|---|---|---|---|---|
CIS Compliance Script (oci-cis-landingzone-quickstart) |
CIS/Best-Practice posture assessment | Python SDK-based checks across many benchmark controls; internal threaded execution | Broad tenancy security/compliance checks, detailed per-finding reports | Higher operational surface (Python env + dependencies), broader/heavier scans that can run significantly longer on large tenancies; no domain-targeted run mode or cache/resume model; limited real-time progress detail during execution | Security/compliance audits, benchmark tracking, governance programs |
showoci (oci-python-sdk/examples/showoci) |
General tenancy inventory exploration | Python SDK sample inventory traversal | Broad resource visibility | Broader and generally slower than focused OCI CLI scraping | Exploration and full-inventory style discovery |
oci-tenancy-review (this repo) |
Fast operational BOM exports for selected domains | OCI CLI + Make fan-out + cacheable artifacts | Targeted compute/storage/database/object-storage/limits/policy views | Minimal runtime dependencies in Cloud Shell, optimized for repeated runs and fast CSV output | Ops reviews, tenancy snapshots, cost/capacity and architecture-oriented analysis |
| Feature | oci-tenancy-review (this repo) |
CIS Compliance Script (oci-cis-landingzone-quickstart) |
showoci (oci-python-sdk/examples/showoci) |
|---|---|---|---|
| Domain-scoped execution (run only one focused domain) | Yes (compute, block-storage, object-storage, base-database, limits, policies, etc.) |
No dedicated domain subcommands; broad report modes via global flags (--raw, --obp, --all-resources) |
Partial via service flags, but oriented to broad service inventory extraction rather than fixed BOM domains |
| Region targeting | Yes (REGIONS, plus per-region commands like compute-region) |
Yes (--regions) |
Yes (-rg / -rgn) |
| Resumable/cache-aware artifact graph | Yes (Makefile target graph with cached outputs and incremental rebuilds) | No make-style cache/resume dependency graph | No make-style cache/resume dependency graph (can export cache snapshot, but not orchestrated incremental rebuild model) |
| Concurrency | Yes (Make fan-out and per-domain orchestration) | Yes (threads and thread pools inside Python collector) | Yes (parallel processing by default, configurable thread count) |
| Progress visibility while running | Clear per-domain/per-region command boundaries and artifact-level checkpoints | Limited; mostly coarse stage logs and end-of-stage counts | Moderate console output, but not focused on artifact checkpointing for resumable runs |
| CSV analyst-friendliness (flat columns vs nested objects) | Optimized for analysis-friendly flattened CSV columns per domain | Raw data CSVs often include nested/object fields (for example compute structures), which makes direct spreadsheet analysis harder without extra transformation | Can export broad CSV data, but many outputs prioritize coverage over tightly normalized, domain-specific flat schemas |
| Minimal Cloud Shell operational footprint | Yes (bash + oci + jq; make optional advanced path) |
No (Python runtime + OCI SDK dependency surface) | No (Python runtime + OCI SDK dependency surface) |
| Read-auditability controls for admins | Strong focus: small Bash entrypoint, explicit OCI command mapping, run log (report/run.log), CI tests enforcing read-only OCI command usage |
Broader compliance engine; auditability is oriented to benchmark findings rather than a small script surface | Broad inventory tool; not focused on a small audited execution surface for targeted exports |
| Primary output model | Targeted BOM CSV artifacts per domain (plus intermediate JSON) | Compliance findings/reports + optional raw data CSV/JSON | General inventory screen/CSV/JSON reporting |
Use this project when you want speed, straightforward auditability, and focused exports.
Use the CIS Compliance Script when you want formal benchmark-aligned compliance depth (at the cost of broader checks and heavier execution).
If you are interested in a guide for easily executing the OCI CIS compliance script via Cloud Shell, checkout my guide.
