
OCI cli – tooling made easy
The OCI tool is easy to use. I had a very interesting conversation with Simon Haslam ( here on Twitter @simon_haslam ) about this. In this blogpost we will highlight the cause (and the solution) of this particular case and in a second part, there is a general workflow which did not let me down for my last deployments.
404 NotAuthorizedOrNotFound
Simon and his colleagues are working with ansible and Terraform for automatic provisioning of their full environment which at a certain point in time failed with a 404 error like this
1 2 3 4 5 6 7 |
ServiceError: { "code": "NotAuthorizedOrNotFound", "message": "Authorization failed or requested resource not found.", "opc-request-id": "2E89***D399", "status": 404 } |
Not very descriptive right? But actually this brings us deeper to how OCID’s are build up.
I take my sandbox environment as an example. We come back to the oci tool later in the post, but I will use it here to show some output. This command lists the databases in my compartment, but using jq, I take the first entry and show all the id’s which are present.
1 2 3 4 5 6 7 |
mbp-pvanpuym:~ pietervanpuymbroeck$ oci db database list --compartment-id ${COMPARTMENT_ID} | jq '.data[1]'|grep -i "id" "compartment-id": "ocid1.compartment.oc1..aaaa****jkulq", "db-home-id": "ocid1.dbhome.oc1.eu-frankfurt-1.abth****uzwwa", "db-system-id": "ocid1.dbsystem.oc1.eu-frankfurt-1.abth****zmhk6a", "id": "ocid1.database.oc1.eu-frankfurt-1.abth****jjyca", "vm-cluster-id": null mbp-pvanpuym:~ pietervanpuymbroeck$ |
Important when dealing with databases, is that you use the Database id. Now you say, “There is no such thing as database ocid”. The answer is “oh yes there is”. The database is “id”.
There is logic behind it and in fact the id’s are really simple to recognise. When you take the ocid it looks like
1 |
"ocid1.compartment.oc1..aaaa****jkulq" |
so first we have
- ocid1 (never saw something else, but feel free to comment)
- This is what the ocid defines. A compartment, dbhome, dbsystem, database,… basically the component that has the id
- the region / availability domain
- the id itself.
This is a pretty clever system as based on this, you (we) could define all we need based on a single OCID. It looks complex, but once you understand how it works it is very simple.
Going into the console, it might lead to confusion when you want to copy paste for using the ocid in the oci tooling.
For example, when we go to my DB systems page:

When you pay close attention, you know that you are copying the DB-system OCID here, but I get why you might think that you copy the Database id. However, the database ocid is a little deeper down.
Click on the db system display name (or view details in the drop down menu)

When you then select “Databases” (down on the left) there you find the “Copy OCID” you are looking for when you need to specify the Database ID in an OCI command.
If you specify another OCID in a command that expects a database id, you might run into 404 NotAuthorizedOrNotFound error. Logical, as it is true! When you look for a database and tell the tool to look in a compartment or something else, it cannot find that resource, hence … Page Not found.
OCI cli
The OCI cli is a useful piece of software, which you can install on your laptop/terminal to manage your resources in your compartment. Digging into the installation of it would bring us a bit too far, but it is outlined here in the documentation.
The installation script will ask you some questions to setup your client for your environment by creating the configuration file. So pay attention to the ocid’s you enter in that setup flow.
So let’s look at the OCI tool itself.
oci works by oci and then specifying what you are interested in. To be honest, I like the tool. I am not a hero in memorising commands, so I type oci db and it gives me the list what I could do with a db.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
mbp-pvanpuym:~ pietervanpuymbroeck$ oci db Usage: oci db [OPTIONS] COMMAND [ARGS]... The CLI for the Database Service. Options: -?, -h, --help For detailed help on any of these individual commands, enter <command> --help. Commands: autonomous-container-database autonomous-data-warehouse **Deprecated.** See [AutonomousDatabase] for... autonomous-data-warehouse-backup **Deprecated.** See [AutonomousDatabaseBackup... autonomous-database An Oracle Autonomous Database. autonomous-database-backup An Autonomous Database backup. autonomous-database-wallet The Autonomous Database wallet details. autonomous-db-preview-version The Autonomous Database preview version. autonomous-db-version The supported Autonomous Database version. autonomous-exadata-infrastructure autonomous-exadata-infrastructure-shape The shape of the Autonomous Exadata... backup A database backup. backup-destination Backup destination details. console-connection data-guard-association The properties that define a Data Guard... database An Oracle database on a bare metal or virtual... db-home exadata-infrastructure ExadataInfrastructure external-backup-job Provides all the details that apply to an... gi-version The Oracle Grid Infrastructure (GI) version. maintenance-run Details of a Maintenance Run. node A server where Oracle Database software is... patch A Patch for a DB system or DB Home. patch-history The record of a patch action on a specified... system The Database Service supports several types... system-shape The shape of the DB system. version The Oracle Database software version. vm-cluster Details of the VM cluster. vm-cluster-network The VM cluster network. mbp-pvanpuym:~ pietervanpuymbroeck$ |
First things first, I would like to have a Data Guard installation. So that starts with a primary database, right? Lets follow the flow to setup a basic data guard installation using oci.
In this post I assume that your tenant and compartment are setup correctly. Also that you have a vnc and subnet configured correctly according your needs and that you have specified correct routing and security lists. As that is OCI Configuration, we won’t cover that here.
Setup the primary database
First of all we need a primary database. I like to use scripts. So to avoid typing too much I used some environment variables
1 2 3 4 5 6 |
export COMPARTMENT_ID=ocid1.compartment.oc1..aaaa****kulq export SUBNET_ID=ocid1.subnet.oc1.eu-frankfurt-1.aaaa****ig5a export PASSWORD="******" export PATH_TO_KEYFILE=******* export PRIM_AD="AAef:EU-FRANKFURT-1-AD-1" export STBY_AD="AAef:EU-FRANKFURT-1-AD-2" |
This clears up the commands a bit as they are pretty long, but that is ok for automation, isn’t it?
The command used for creating a VM based (entry level for oci) primary database would look like this for me
1 |
oci db system launch --display-name 'CLDGDEMOAD1' --compartment-id ${COMPARTMENT_ID} --availability-domain ${PRIM_AD} --subnet-id ${SUBNET_ID} --shape VM.Standard2.2 --node-count 1 --cpu-core-count 2 --initial-data-storage-size-in-gb 256 --hostname vmcldgdemovmad1 --database-edition ENTERPRISE_EDITION_HIGH_PERFORMANCE --license-model LICENSE_INCLUDED --db-name CLDGDEMO --db-version 19.6.0.0 --character-set AL32UTF8 --ncharacter-set AL16UTF16 --db-workload OLTP --pdb-name MYPDB --admin-password ${PASSWORD} --ssh-authorized-keys-file ${PATH_TO_KEYFILE}/id_rsa.pub |
When you launch the command, it returns a json, which you could parse if you would want to.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
mbp-pvanpuym:~ pietervanpuymbroeck$ oci db system launch --display-name 'CLDGDEMOAD1' --compartment-id ${COMPARTMENT_ID} --availability-domain ${PRIM_AD} --subnet-id ${SUBNET_ID} --shape VM.Standard2.2 --node-count 1 --cpu-core-count 2 --initial-data-storage-size-in-gb 256 --hostname vmcldgdemovmad1 --database-edition ENTERPRISE_EDITION_HIGH_PERFORMANCE --license-model LICENSE_INCLUDED --db-name CLDGDEMO --db-version 19.6.0.0 --character-set AL32UTF8 --ncharacter-set AL16UTF16 --db-workload OLTP --pdb-name MYPDB --admin-password ${PASSWORD} --ssh-authorized-keys-file ${PATH_TO_KEYFILE}/id_rsa.pub { "data": { "availability-domain": "AAef:EU-FRANKFURT-1-AD-1", "backup-network-nsg-ids": null, "backup-subnet-id": null, "cluster-name": null, "compartment-id": "ocid1.compartment.oc1..aaaa****kulq", "cpu-core-count": 2, "data-storage-percentage": 80, "data-storage-size-in-gbs": 256, "database-edition": "ENTERPRISE_EDITION_HIGH_PERFORMANCE", "db-system-options": { "storage-management": "ASM" }, "defined-tags": {}, "disk-redundancy": "HIGH", "display-name": "CLDGDEMOAD1", "domain": "pvanpuym.vcnpvanpuym.oraclevcn.com", "fault-domains": [ "FAULT-DOMAIN-2" ], "freeform-tags": {}, "hostname": "vmcldgdemovmad1", "id": "ocid1.dbsystem.oc1.eu-frankfurt-1.abth****vmp7q", "iorm-config-cache": null, "last-patch-history-entry-id": null, "license-model": "LICENSE_INCLUDED", "lifecycle-details": null, "lifecycle-state": "PROVISIONING", "listener-port": 1521, "node-count": 1, "nsg-ids": null, "reco-storage-size-in-gb": 256, "scan-dns-record-id": null, "scan-ip-ids": null, "shape": "VM.Standard2.2", "sparse-diskgroup": null, "ssh-public-keys": [ "ssh-rsa AAAA****com" ], "subnet-id": "ocid1.subnet.oc1.eu-frankfurt-1.aaaa****ig5a", "time-created": "2020-04-21T18:18:52.323000+00:00", "time-zone": "UTC", "version": null, "vip-ids": null }, "etag": "36***d1", "opc-work-request-id": "ocid1.coreservicesworkrequest.oc1.eu-frankfurt-1.abth****ftna" } mbp-pvanpuym:~ pietervanpuymbroeck$ |
After a while, the primary database is ready to use.
With this command you can find the database ocid:
1 |
oci db database list --compartment-id ${COMPARTMENT_ID} | jq '.data[]'| jq -c '[."db-name", .id, ."lifecycle-state"]' |
Creating the standby
I have exported that Database id in the ${NEW_DB_ID} environment variable and then creating a Data Guard association, with a new db system is as simple as running one command:
1 |
oci db data-guard-association create with-new-db-system --availability-domain ${STBY_AD} --creation-type NewDbSystem --database-admin-password ${PASSWORD} --database-id ${NEW_DB_ID} --display-name "CLDGDEMOAD2" --hostname "vmcldgdemovmad2" --protection-mode MAXIMUM_PERFORMANCE --subnet-id ${SUBNET_ID} --transport-type ASYNC --shape "VM.Standard2.2" |
This also return you a Json:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
mbp-pvanpuym:~ pietervanpuymbroeck$ oci db data-guard-association create with-new-db-system --availability-domain ${STBY_AD} --creation-type NewDbSystem --database-admin-password ${PASSWORD} --database-id ${NEW_DB_ID} --display-name "CLDGDEMOAD2" --hostname "vmcldgdemovmad2" --protection-mode MAXIMUM_PERFORMANCE --subnet-id ${SUBNET_ID} --transport-type ASYNC --shape "VM.Standard2.2" { "data": { "apply-lag": null, "apply-rate": null, "database-id": "ocid1.database.oc1.eu-frankfurt-1.abth****47cq", "id": "ocid1.dgassociation.oc1.eu-frankfurt-1.abth****qufa", "lifecycle-details": null, "lifecycle-state": "PROVISIONING", "peer-data-guard-association-id": null, "peer-database-id": null, "peer-db-home-id": null, "peer-db-system-id": null, "peer-role": "STANDBY", "protection-mode": "MAXIMUM_PERFORMANCE", "role": "PRIMARY", "time-created": "2020-04-21T20:28:47.892000+00:00", "transport-type": "ASYNC" }, "etag": "82***f6", "opc-work-request-id": "ocid1.coreservicesworkrequest.oc1.eu-frankfurt-1.abth****cb54a" } mbp-pvanpuym:~ pietervanpuymbroeck$ |
Then you need to wait a while again and upon completion of this process, your Data Guard installation is ready for use!
Verifying the Data Guard Association
The proof of the pudding is the eating. Not only in the browser you can see the Data Guard association

Also using the cli you can show the Data Guard association using following command
1 |
oci db data-guard-association list --database-id ${NEW_DB_ID} |
And yes! Correct, this also returns a json with the status
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
mbp-pvanpuym:~ pietervanpuymbroeck$ oci db data-guard-association list --database-id ${NEW_DB_ID} { "data": [ { "apply-lag": "0 seconds", "apply-rate": "8.00 KByte/s", "database-id": "ocid1.database.oc1.eu-frankfurt-1.abth****47cq", "id": "ocid1.dgassociation.oc1.eu-frankfurt-1.abthe****qufa", "lifecycle-details": null, "lifecycle-state": "AVAILABLE", "peer-data-guard-association-id": "ocid1.dgassociation.oc1.eu-frankfurt-1.abth****vfuza", "peer-database-id": "ocid1.database.oc1.eu-frankfurt-1.abthe****jyca", "peer-db-home-id": "ocid1.dbhome.oc1.eu-frankfurt-1.abthe****zwwa", "peer-db-system-id": "ocid1.dbsystem.oc1.eu-frankfurt-1.abthe****zmhk6a", "peer-role": "STANDBY", "protection-mode": "MAXIMUM_PERFORMANCE", "role": "PRIMARY", "time-created": "2020-04-21T20:28:47.892000+00:00", "transport-type": "ASYNC" } ] } mbp-pvanpuym:~ pietervanpuymbroeck$ |
Conclusion
Of course the mandatory disclaimer, make sure you setup your backups and all the rest of the necessary good practices when you take this in production. This is just meant to show the bare necessities to make it working in the cloud. A lot more practical and good documentation for DBCS is available here. To use Oracle Data Guard with Exadata, see Using Oracle Data Guard with Exadata DB Systems.
The bottom line is, this works very well and I was also very pleased to see how easy it is to setup full installations. Also setting up a RAC to RAC Data Guard suddenly becomes a very easy task. I think this is useful to automate the setup process and when you want a small environment, perfect way to achieve this. For bigger installations, of course we advise to use ExaCS, but that story is for a future blog post.
As always, questions, remarks?
find me on twitter @vanpupi