Wednesday, July 22, 2026

OUD 14c: Updating Best Practices


One of the most important parts of operationalization is keeping software up-to-date.  With regards to Oracle Unified Directory (OUD), this comes in two forms... applying the latest OPatch and OUD patches and keeping the Java Development Kit (JDK) up-to-date.   The optional web-based administrative interface to OUD, the Oracle Unified Directory Services Manager (OUDSM). includes the same patching requirements of OUD plus a few additional patches for Fusion Middleware (FMW) Infrastructure (a.k.a. WLS) and the Application Developer Framework (ADF).  In this blog post, I'm going to walk you through what I do from a best practices perspective to keep OUD 14c and OUDSM 14c up-to-date.

Please note that although, these are the best practices that I've accrued over time working with OUD, they are note officially endorsed by Oracle or Oracle Support and should be treated and qualified as such.

Pre-qualify All Updates

Before getting into the details of the update process, pre-qualify the updates and update processes in one or more lower environments before attempting in production.  This typically entails ensuring clean start and stop of OUD and OUDSM.  It also means conducting a basic battery of functional tests to ensure that OUD and OUDSM are functioning properly.  And if appropriate, use load generation software to confirm that OUD is performing at least as good as it was before the application of the patches.


BEST PRACTICE: ALWAYS pre-qualify patches and change control in lower environments before applying to production environments.

Get Updates

The first step of any update process is getting all of the relevant updates and uploading them to the OUD hosts to be updated.  In all of the examples that I'm about to provide, I put the downloaded JDK and patches into /u01/bits.  Now let's iterate through the downloads that we will need.

BEST PRACTICE: The authoritative support document for determining relevant patches for standalone installations of OUD and OUDSM is KA1182.  Consult this support document to determine the full list of patches to apply to the specified version of either OUD or OUDSM.

OUD 14.1.2.1
--> Identity & Access Management --> Oracle Unified Directory 14.1.2.1 Standalone

OUDSM 14.1.2.1
--> Identity & Access Management --> Oracle Unified Directory 14.1.2.1 Standalone
--> FMW Infrastructure --> FMW Infrastructure 14.1.2.0


Always Find The Latest (Non-Superseded) Component Patch Number

This blog post was written at a single point in time. The patch numbers cited may have changed since the post was published. Therefore, it is always good to confirm that the stated patch number for a given component is the latest non-superseded patch number for that component.  To find the latest patch number for a given component, navigate to the Patches column of the https://support.oracle.com page and enter the patch number into the Patch Search form, enter tab key on your keyboard and then click on Apply button to see the requested patch.  For example, if we search on patch 39486835:


The search result indicates in patch has been Superseded.


Clicking on the 39486835 link reveals that patch 39486835 has been superseded by patch 39605232.


Therefore, we want to search in the Patch Search tool for 39605232 to see if it is the latest patch.  We find as of July 2026 patch 39605232 is the latest version for the OUD 14.1.2.1 Bundle Patch.

Repeat this process for each of the patches specified for OUD 14c and OUDSM 14c in support document KA1182.


BEST PRACTICE:
As a side note, if you already know the desired patch number, you can navigate more quickly to the specific patch number download page with the following web link syntax:

https://updates.oracle.com/download/<patch_number>.html

Or search on the patch with the following support search tool:

https://support.oracle.com/support/?patchId=<patch_number>

JDK Updates

Download the latest JDK 21 (or 17) from the Java Downloads page.

Note that you can directly download either JDK 21 with your preferred command line browser tool like curl. For example:

curl -kso /u01/bits/jdk21.tgz https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz

OPatch Updates

Some OUD and OUDSM bundle patches require an updated version of the OPatch application.  Download the latest OPatch update at this link (https://updates.oracle.com/download/28186730.html) and upload to the OUD/OUDSM host. I put this patch (p28186730_1394224_Generic.zip) in /u01/bits in my system.

OUD Updates

According to support document KA1182, the following patches need to be downloaded and applied to OUD middleware home:
  • OPatch (28186730) - p28186730_1394224_Generic.zip
  • FMW THIRD-PARTY BUNDLE PATCH (39604460) - p39604460_141200_Generic.zip
  • FMW DBCLIENT (39264447) - p39264447_141200_Linux-ARM-64.zip 
  • JDBC 23.26.2 BUNDLE PATCH (39487373) -  p39487373_141200_Generic.zip
  • RDA (39246777) - p39246777_141200_Generic.zip
  • OUD Bundle Patch (39605232) - p39605232_141210_Generic.zip
Download each of the patches from Oracle Support at https://support.oracle.com and put the downloaded zip files into /u01/bits/oud and extract each one into into /u01/sw/oud and then use opatch to apply each one.  If the specified patch has been replaced with a newer patch number, then the Classification column of the results page will say Superseded. 


OUDSM Updates

According to support document KA1182, the following OUD 14c patches need to be downloaded and applied to OUDSM middleware home:
As well as the following Fusion Middleware Infrastructure 14c patches:
Download each of the patches from Oracle Support at https://support.oracle.com and put the downloaded zip files into /u01/bits/oudsm and extract each one into into /u01/sw/oudsw and then use opatch to apply each one to the OUDSM middleware home (/u01/mw_oudsm14c).

BEST PRACTICE:
 The security and performance principles of minimization and avoiding un-necessary resource contention prescribe that it is best to deploy OUDSM on a separate host from the OUD host(s).  And if OUDSM does need to co-exist on the same host as OUD, it is best for OUDSM to have its own middleware home and JDK so that the OUDSM software can be updated independently of OUD.

Update Java Development Kit

JDK is the foundation of OUD and OUDSM and thus is one of the most important components to keep up-to-date. The supported JDK long term support release (LTS) versions for OUD 14c are 21 and 17. I generally prefer to use the most recent JDK LTS release available. Therefore, I'll focus on JDK 21.  The latest versions of JDK 21 are available from Oracle at Oracle Java Downloads. One of the interesting things about the JDK 21 download is that the download file names are the same over time.  That makes download automation easier.  For example, to determine on x64 Linux systems, I use a script similar to the following to check to see if the latest JDK 21 version is newer than the version that I have installed.

In this example, the current JDK used for OUD is located in /u01/sw/oud_jdk.  I create a temporary directory to store the downloaded and extracted JDK version.  Once the new JDK is extracted, I check to see if the version is newer than the current JDK version and if so, return a message that says "JDK Version ... is newer".

jdir='oud_jdk'

swdir=/u01/sw

cver=$(${swdir}/${jdir}/bin/java -version 2>&1 |grep "^java version"|cut -d'"' -f2|sed -e "s/^21\.//g")

mkdir ${swdir}/ckjdk.tmp

curl -kso ${swdir}/
ckjdk.tmp/jdk21.tgz https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz

tar -xzof ${swdir}/
ckjdk.tmp/jdk21.tgz -C ${swdir}/ckjdk.tmp

nver=$(${swdir}/
ckjdk.tmp/*/bin/java -version 2>&1 |grep "^java version"|cut -d'"' -f2|sed -e "s/^21\.//g")

if [ $(echo "${cver} - ${nver}" |bc) != '0'  ];then echo "This JDK Version 21.${nver} is newer";fi


BEST PRACTICE: Before moving on to the procedure for updating the JDK, it is important to note that one best practice in this example is to use a generic name for the JDK path like /u01/sw/oud_jdk rather than the default path name /u01/sw/jdk-21.0.10 before installing and setting up OUD or OUDSM instances. That will greatly simplify updating the JDK as well as upgrading OUD in the future.

Roll JDK Forward

If the version available from Oracle is newer, then I use the following procedure to update OUD.  Note that similarly with OUDSM, you just stop OUDSM, update the JDK and start OUDSM back up.

1. Stop the existing OUD instances with stop-ds including Replication Gateway, Proxy and OUD Storage.

/u01/mw_oud14c/replgw1/OUD/bin/stop-ds
/u01/mw_oud14c/proxy1/OUD/bin/stop-ds
/u01/mw_oud14c/oud1/OUD/bin/stop-ds
/u01/mw_oud14c/asinst_1/OUD/bin/stop-ds

2. Confirm that no lingering processes processes are referencing the JDK.  If the OUDSM is running using this JDK, you will want to shut that down as well.

ps -ef|grep /u01/sw/${jdir}

If there are any processes still running with the existing JDK, gracefully stop those processes before proceeding to replace the JDK.

3. Re-locate the existing JDK making note of the resulting JDK badkup date (<YYYYMMDDHHMMSS>).

now=$(date +'%Y%m%d%H%M%S')
mv 
${swdir}/${jdir} ${swdir}/${jdir}.${now}
ls -ald 
${swdir}/${jdir}.${now}

4. Move the new JDK version into place.

mv ${swdir}/ckjdk.tmp/jdk-21* /u01/sw/${jdir}
rm -fr 
${swdir}/ckjdk.tmp

5. Start the OUD instances back up with start-ds.

/u01/mw_oud14c/asinst_1/OUD/bin/start-ds
/u01/mw_oud14c/oud1/OUD/bin/start-ds
/u01/mw_oud14c/proxy1/OUD/bin/start-ds
/u01/mw_oud14c/replgw1/OUD/bin/start-ds

Roll JDK Back

If for any reason the new JDK does not work, you will want to roll back. The beauty of this best practice method of managing the JDK version is that it is effortless and efficient to roll back to the previous version.  Here is the procedure for rolling the JDK back to the previous version.

1. Stop the existing OUD instances with stop-ds including Replication Gateway, Proxy and OUD Storage.

/u01/mw_oud14c/replgw1/OUD/bin/stop-ds
/u01/mw_oud14c/proxy1/OUD/bin/stop-ds
/u01/mw_oud14c/oud1/OUD/bin/stop-ds
/u01/mw_oud14c/asinst_1/OUD/bin/stop-ds

2. Confirm that no lingering processes are referencing the JDK

jdir='oud_jdk'
ps -ef|grep /u01/sw/${jdir}

If there are any processes still running with the existing JDK, gracefully stop those processes before proceeding to replace the JDK.

3. Re-locate the existing JDK to a folder name ending with .failed to indicate that for whatever reason, that version was incompatible so that you will know which one to troubleshoot in a lower environment.

now=$(date +'%Y%m%d%H%M%S')
mv ${swdir}/${jdir} ${swdir}/${jdir}.${now}.failed

4. Restore the backup JDK (<YYYYMMDDHHMMSS>) back into place

mv ${swdir}/${jdir}.<YYYYMMDDHHMMSS> ${jdir}

5. Start the OUD instances back up with start-ds.

/u01/mw_oud14c/asinst_1/OUD/bin/start-ds
/u01/mw_oud14c/oud1/OUD/bin/start-ds
/u01/mw_oud14c/proxy1/OUD/bin/start-ds
/u01/mw_oud14c/replgw1/OUD/bin/start-ds

Update Oracle Unified Directory

Here is the procedure that I use for applying patches to OUD including bundle patches.

Roll OUD Forward

1. Determine the current OUD release version and patch bundle installed with "start-ds --version".  In most cases, the release version will be 14.1.2.1.<date> where the date represents the most recent patch bundle installed.  In the following sample, output, the patch bundle installed was the January 2nd, 2025 patch bundle (251202).

/u01/mw_oud14c/oud1/OUD/bin/start-ds --version
Oracle Unified Directory 14.1.2.1.251202
Build 20251202172439Z

2. In addition to getting the OUD version, store the current list of patches in an output file that you can reference when determining if a patch needs to be applied or not.

export JAVA_HOME=/u01/sw/jdk
export ORACLE_HOME=/u01/mw_oud14c
$ORACLE_HOME/OPatch/opatch lsinventory > /u01/oud_lsinventory.txt
Oracle Unified Directory 14.1.2.1.251202
Build 20251202172439Z

BEST PRACTICE: Note that some OUD patches require an updated version of OPatch to be installed before the OUD patch can be applied. Therefore, it is a best practice to go ahead and download the latest OPatch version (https://updates.oracle.com/download/28186730.html) in addition to the OUD patch(es). 

3. Before applying any patches, stop the OUD instance(s) and make a backup of the middleware home so that you have a roll back path in case the patch fails or the update(s) introduce unexpected problems. OUD instance types may include OUD storage, OUD proxy, or OUD replication gateway depending on your deployment.

/u01/mw_oud14c/replgw1/OUD/bin/stop-ds
/u01/mw_oud14c/proxy1/OUD/bin/stop-ds
/u01/mw_oud14c/oud1/OUD/bin/stop-ds
/u01/mw_oud14c/asinst_1/OUD/bin/stop-ds

4. Confirm that no lingering processes processes are referencing the JDK.  If the OUDSM is running using this JDK, you will want to shut that down as well.

jdir='oud_jdk'
ps -ef|grep /u01/sw/${jdir}

If there are any processes still running with the existing JDK, gracefully stop those processes before proceeding to backup the OUD middleware home.

5. Backup the OUD middleware home.  Make note of the backup date (<backup_date>) in case you need to roll back to this backup.

now=$(date +'%Y%m%d%H%M%S')
cd /u01
tar -czf mw_oud14c.${now}.tgz mw_oud14c

BEST PRACTICE: Make sure that the backup file destination has sufficient storage for the OUD middleware home backup.  You may need to store the backup file (mw_oud14c.<date>.tgz) in a different filesystem if the OUD home is large enough that the backup would fill up the filesystem.  You can determine the size consumed by the middleware home with the du command. For example: "du -hs /u01/mw_oud14c"

Compare that size to the amount of free space on the filesystem on which the middleware home resides with the df command. For example: "df -h /u01/mw_oud14c"

Using the example commands, we see from the output that the middleware home (/u01/mw_oud14c) is 717M and the available storage on the filesystem is 132GB, which is more than enough to hold the backup of the middleware home.

du -hs /u01/mw_oud14c
717M /u01/mw_oud14c

df -h /u01/mw_oud14c
Filesystem                  Size  Used Avail Use% Mounted on
/dev/mapper/ocivolume-root  189G   58G  132G  31% /

6. Apply the OPatch update.  The application of the OPatch update is different than normal OUD patches.  The following procedure illustrates how to apply the OPatch update.  In this example, the downloaded OPatch update file from Oracle Support (https://support.oracle.com) is p28186730_1394224_Generic.zip. Use the patch file name that you download in place of this file name in the following example.

jdir='oud_jdk'
export JAVA_HOME=/u01/sw/${jdir}
export ORACLE_HOME=/u01/mw_oud14c
mkdir /u01/sw/opatch
unzip -qd /u01/sw/opatch -ou /u01/bits/opatch/
p28186730_1394224_Generic.zip
$JAVA_HOME/bin/java -jar /u01/sw/opatch/6880880/opatch_generic.jar -silent oracle_home=$ORACLE_HOME

7. Apply each of the patch to the OUD 14c middleware home.  To apply the OUD patch, use the opatch command.  Set the requisite environment variables (JAVA_HOME and ORACLE_HOME), extract patch, cd into the patch number directory and then apply with the opatch command.  Once the application of the patch is complete, the last line of the opatch command output should be some variation of "OPatch succeeded" or "Patch ... successfully applied"  If this is not the case, review the output from the opatch command to see if there are any dependencies to be satisfied or issues to resolve.  Resolve these and then apply the opatch until success is attained.  If you can not resolve the issue, open an Oracle Support Request to obtain assistance from support to troubleshoot and resolve the issue.

Here is an automated procedure to iterate through all of the patches in /u01/bits/oud14c.

jdir='oud_jdk'
export JAVA_HOME=/u01/sw/${jdir}
export ORACLE_HOME=/u01/mw_oud14c
mkdir /u01/sw/oud14c
cd /u01/bits/oud14c
for p in p*
do patch=$(echo ${p}|cut -d'p' -f2|cut -d'_' -f1)
   unzip -qod /u01/sw/oud14c /u01/bits/oud14c/${p}
   cd /u01/sw/oud14c/${patch}
   $ORACLE_HOME/OPatch/opatch apply -silent
done


8. Once the patch(es) have been successfully applied, then start the OUD instances back up, which may include OUD storage, OUD proxy, or OUD replication gateway depending on your deployment.

/u01/mw_oud14c/asinst_1/OUD/bin/start-ds
/u01/mw_oud14c/oud1/OUD/bin/start-ds
/u01/mw_oud14c/proxy1/OUD/bin/start-ds
/u01/mw_oud14c/replgw1/OUD/bin/start-ds

Roll OUD Back

If there are any issues with the application of the opatch or OUD updates, you may need to roll back to the previous state. This fortunately, is easy because you made a backup of the middleware home. Here is the roll back procedure.

1. Stop OUD
/u01/mw_oud14c/replgw1/OUD/bin/stop-ds
/u01/mw_oud14c/proxy1/OUD/bin/stop-ds
/u01/mw_oud14c/oud1/OUD/bin/stop-ds
/u01/mw_oud14c/asinst_1/OUD/bin/stop-ds

2. Confirm that no lingering processes processes are referencing the OUD JDK.  If the OUDSM is running using this JDK, you will want to shut that down as well.

jdir='oud_jdk'
ps -ef|grep /u01/sw/${jdir}

If there are any processes still running with the existing JDK, gracefully stop those processes before proceeding to restore the OUD middleware home.

3. Re-locate the existing OUD middleware home
now=$(date +'%Y%m%d%H%M%S')
mv /u01/mw_oud14c /u01/mw_oud14c.${now}

4. Restore the previous OUD middleware home.
cd /u01
tar -xzf /u01/mw_oud14c.
<backup_date>.tgz

5. Start OUD back up
/u01/mw_oud14c/asinst_1/OUD/bin/start-ds
/u01/mw_oud14c/oud1/OUD/bin/start-ds
/u01/mw_oud14c/proxy1/OUD/bin/start-ds
/u01/mw_oud14c/replgw1/OUD/bin/start-ds


Update Oracle Unified Directory Services Manager

Oracle Unified Directory Services Manager (OUDSM) is composed of the combination of the JDK, Oracle Unified Directory (OUD), Fusion Middleware (FMW) Infrastructure (a.k.a. WebLogic) and the Application Developer Framework (ADF) content.

BEST PRACTICE: It is best practice to install OUDSM in its own middleware home on its own host that is separate and independent of the hosts where OUD instances are running.  The reasons for this separation include the following:
  • Maintain independent JDK versions from other products. Note that if you are co-locating the OUD instances on the same host, use a different JDK path like /u01/sw/oudsm_jdk instead of /u01/sw/jdk referenced earlier in this post.
  • Maintain independent OUD patch applications from other products
  • Avoid resource contention with other products
  • Preserve the security best practice of minimization by only installing and running what is minimally necessary to run the desired service or software

JDK Update

Apply the same JDK update procedures documented above. However, note that if OUDSM and OUD are on the same host, the JDK best practice is to have a separate JDK directory for OUDSM from other products on the same host. For example, consider using /u01/sw/oud_jdk for OUD and /u01/sw/oudsm_jdk for OUDSM.   If the JDK update presents any problem with starting or running OUDSM, roll back the JDK with the "Roll JDK Back" procedure documented in the "Update Java Development Kit" section of this blog post.

OUD Patches

The procedure for applying the OUD patch to OUDSM middleware home (Example: /u01/mw_oudsm14c) is identical to those cited earlier with the exception of using the OUDSM middleware home directory and OUDSM specific JDK path (Example: /u01/mw_oudsm14c and /u01/sw/oudsm_jdk) that that are separate and independent of the OUD middleware home and JDK path (Example: /u01/mw_oud14c and /u01/sw_oud). Therefore, follow those procedures for applying the OUD patch to the OUDSM middleware home.  Similarly to the JDK, if you need to roll back to the previous JDK version, use the "Roll OUD Back" but for the OUDSM middleware home (/u01/mw_oudsm14c).

FMW Patches

In addition to the OUD patches, OUDSM has several of its own FMW Infrastructure (a.k.a. WebLogic or WLS) patches to apply as well.

SPECIAL NOTE: If you are installing OUDSM for the first time, you may need to open a support request to obtain the password for the Oracle Application Development Framework (ADF) one off patch 37376076 (https://updates.oracle.com/download/37376076.html) that isis required to run OUDSM 14c.

Roll OUDSM Forward

1. Before applying any patches, stop the OUDSM and make a backup of the middleware home so that you have a roll back path in case the patch fails or introduces unexpected problems. 

/u01/mw_oudsm14c/domains/oudsm/bin/stopWebLogic.sh

ps -ef|grep -i weblogic.NodeManager|grep -v grep|awk '{ print $2 }'|xargs -n1 kill -9


2. Backup the OUDSM middleware home.  Make note of the backup date (<backup_date>) in case you need to roll back to this backup.

now=$(date +'%Y%m%d%H%M%S')
cd /u01
tar -czf mw_oudsm14c_fmw.${now}.tgz mw_oudsm14c

BEST PRACTICE: Make sure that the backup file destination has sufficient storage for the OUDSM middleware home backup.  You may need to store the backup file (mw_oudsm14c_fmw.<date>.tgz) in a different filesystem if the OUDSM home is large enough that the backup would fill up the filesystem.  You can determine the size consumed by the middleware home with the du command. For example: "du -hs /u01/mw_oudsm14c"

Compare that size to the amount of free space on the filesystem on which the middleware home resides with the df command. For example: "df -h /u01/mw_oudsm14c"

Using the example commands, we see from the sample command output that the middleware home (/u01/mw_oudsm14c) is 3.5G and the available storage on the filesystem is 126GB, which is more than enough to hold the backup of the middleware home.

du -hs /u01/mw_oudsm14c
3.5G /u01/mw_oudsm14c

df -h /u01/mw_oudsm14c
Filesystem                  Size  Used Avail Use% Mounted on
/dev/mapper/ocivolume-root  189G   64G  126G  34% /

3. Apply the current OPatch update.  The application of the OPatch update is different than normal OUD patches.  The following procedure illustrates how to apply the OPatch update.  In this example, the downloaded OPatch update file is p28186730_1394224_Generic.zip from Oracle Support (https://support.oracle.com). Use the patch file name that you download in place of this file name in the following example.

jdir='oudsm_jdk'
export JAVA_HOME=/u01/sw/${jdir}
export ORACLE_HOME=/u01/mw_oudsm14c
mkdir /u01/sw/opatch
unzip -qd /u01/sw/opatch -ou /u01/bits/opatch/p28186730_1394224_Generic.zip
$JAVA_HOME/bin/java -jar /u01/sw/opatch/6880880/opatch_generic.jar -silent oracle_home=$ORACLE_HOME

4. Apply OUD 14c and OUDSM 14c patches to the OUDSM middleware home (/u01/mw_oudsm14c).
To apply a patch, use the opatch command.  Set the requisite environment variables (JAVA_HOME and ORACLE_HOME), extract patch, cd into the patch number directory and then apply with the opatch command.  Once the application of the patch is complete, the last line of the opatch command output should be some variation of "OPatch succeeded" or "Patch ... successfully applied"  If this is not the case, review the output from the opatch command to see if there are any dependencies to be satisfied or issues to resolve.  Resolve these and then apply the opatch until success is attained.  If you can not resolve the issue, open an Oracle Support Request to obtain assistance from support to troubleshoot and resolve the issue.

Here is an automated procedure to iterate through all of the patches in /u01/bits/oudsm14c.

jdir='oudsm_jdk'
export JAVA_HOME=/u01/sw/${jdir}
export ORACLE_HOME=/u01/mw_oudsm14c
mkdir /u01/sw/oudsm14c
cd /u01/bits/oudsm14c
for p in p*
do patch=$(echo ${p}|cut -d'p' -f2|cut -d'_' -f1)
   unzip -qod /u01/sw/oudsm14c /u01/bits/oudsm14c/${p}
   cd /u01/sw/oudsm14c/${patch}
   $ORACLE_HOME/OPatch/opatch apply -silent
done

 
5. Once the patch(es) have been successfully applied, then start OUDSM back up.

now=$(date +'%Y%m%d%H%M%S')

/u01/mw_oudsm14c/domains/oudsm/bin/startWebLogic.sh > /u01/logs/wl-${now}.log 2>&1 &

# Wait until WebLogic completes starting up signified by seeing "in RUNNING mode" in the wl-${now}.log file.

# Start Node Manager
/u01/mw_oudsm14c/domains/oudsm/bin/startNodeManager.sh 
> /u01/logs/nm-${now}.log 2>&1 &

Roll OUDSM Back

If there are any issues with the application of the opatch or WLS updates, you may need to roll back to the previous state. This fortunately, is easy enough because you made a backup of the middleware home. Here is the roll back procedure.

1. Stop OUDSM
/u01/mw_oudsm14c/domains/oudsm/bin/stopWebLogic.sh

ps -ef|grep -i weblogic.NodeManager|grep -v grep|awk '{ print $2 }'|xargs -n1 kill -9

2. Re-locate the existing OUDSM middleware home
now=$(date +'%Y%m%d%H%M%S')
mv /u01/mw_oudsm14c /u01/mw_oudsm14c.${now}

3. Restore the previous OUDSM middleware home.
cd /u01
tar -xzf /u01/mw_oudsm14c_fmw.
<backup_date>.tgz

4. Start OUDSM back up
now=$(date +'%Y%m%d%H%M%S')

/u01/mw_oudsm14c/domains/oudsm/bin/startWebLogic.sh > /u01/logs/wl-${now}.log 2>&1 &

# Wait until WebLogic completes starting up signified by seeing "in RUNNING mode" in the wl-${now}.log file.

# Start Node Manager
/u01/mw_oudsm14c/domains/oudsm/bin/startNodeManager.sh
> /u01/logs/nm-${now}.log 2>&1 &

Qualify Functional And Performance Tests

Once the updates are complete, run through a battery of functional and performance tests to ensure that OUD and OUDSM are functioning properly.  In addition, if appropriate, perform load generation testing to confirm that OUD is performing as well as it did before the application of patches.

Clean Up

Once qualification testing is complete and sufficient time has passed that you feel confident that you will not need to roll back the updates, then you can remove the backup files created during the update process. Typical retention of these backup files in my experience is 1-2 weeks.

I hope you find this beneficial and insightful.

Blessings!

Tuesday, July 14, 2026

OCI IAM: Python client examples


The same customer from my previous Node.js client post also wanted a python client example that authenticates to the Oracle AI Database via access token by Oracle Cloud Identity & Access Management (OCI IAM) integration.  This time around, I decided to take a different approach. Instead of starting with a template and refining from there, I used an Artificial Intelligence (AI) Code Generation Tool to create two python scripts where one was based on the thick driver and the second is based on the thin driver.

Driver Differences

There are two fundamental differences between thick and thin drivers. The most significant difference is that the thick driver requires an installation of the Oracle AI Database Instant Client and the thin driver does not. The second difference is that the thick driver can read and use the TLS certificate from the Oracle client wallet for TLS connection to the Oracle AI Database.  This is helpful if the TLS certificate is self-signed because the thin driver does not support self-signed certificates.  Or, more specifically, the underlying OpenSSL driver leveraged by the thin driver does not support self-signed certificates.

Benefits Of Using AI Code Generator

In the past, I've used AI code generation tools for refining or understanding a variety of shell scripting use cases. However, I had never asked an AI Code Generator to write sophisticated code like this. Given that I am not proficient in python, this felt like a great opportunity to learn.  Therefore, I started with the following prompt:

Write python3.14 script that opens a connection to Oracle database using oracledb module and authenticates using OCI IAM token via oracledb.plugins.oci_tokens module and the database connect string is read in from a TNS alias from tnsnames.ora and executes the following SQL: SELECT SYS_CONTEXT ('USERENV','AUTHENTICATED_IDENTITY') FROM DUAL

The OCI IAM access token is acquired independently of the python script by "oci iam db-token get".  The python script can read the orivate key written at /Users/dbuser/.oci/db-token/oci_db_key.pem and the db-token written at: /Users/dbuser/.oci/db-token/token. 

I was shocked that it produced solid code and that it almost worked from the first prompt.  I iterated a few times to add additional clarity before arriving at the the first script that leverages the thick driver.

The thing that really impressed me beyond getting the core functionality to work was how easy that it was through prompting to add features such as adding arguments to pass to the script to specify things like the TNS alias name to use, where to find the tnsnames.ora, add SQL statement to execute, and much more.  Given my lack of python proficiency, that was amazing how quickly and simply that it added those features. And if you watch the process that it goes through to craft the code, it is quite an impressive decision tree and revision process that it goes through all on its own.

The last thing that I'll say is that I was shocked how well it created built-in documentation and usage for each script.

Please note that I didn't write a single line of code for either script.

Python3.14 Virtual Environment

The oracledb python library requires python3.10 or newer. In my case on a MacOS laptop, I tested with python3.14.  The Homebrew installer does not allow installation of . releases such as .14 of python3.  To install the requisite libraries in this context, you have to setup and activate the virtual python3.14 environment with the following.

mkdir $HOME/.oracle/pytoken
cd $HOME/.oracle/pytoken
python3.14 -m venv .venv

source .venv/bin/activate
pip install oracledb
pip install oci
pip install requests

Whenever you want to run the script, you will need to activate the virtual environment so that it can find the requisite libraries.

cd $HOME/.oracle/pytoken
source .venv/bin/activate

Having said that, after everything was done, I went back and changed the script headers from python3.14 to python3 and for reasons not yet clear to me, it worked.
From:

#!/usr/bin/env python3.14

To:

#!/usr/bin/env python3

I'm not yet sure why it worked but it did for both the thick and thin scripts.

Thick Oracle DB Driver Client

Here's the resulting script

$ cat ociiam-python-thick.py
#!/usr/bin/env python3.14
"""
Connect to Oracle Database using an OCI IAM token, a TNS alias, and an
auto-login Oracle wallet in Thick mode.

Expected locations:
- Instant Client:      /Users/dbuser/.oracle/instant_client
- Wallet directory:    /Users/dbuser/.oracle/admin/client_wallet
- Token file:          /Users/dbuser/.oci/db-token/token
- Private key file:    /Users/dbuser/.oci/db-token/oci_db_key.pem
"""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

import oracledb


DEFAULT_TOKEN_FILE = Path("/Users/
dbuser/.oci/db-token/token")
DEFAULT_KEY_FILE = Path("/Users/dbuser/.oci/db-token/oci_db_key.pem")
DEFAULT_WALLET_DIR = Path("/Users/dbuser/.oracle/admin/client_wallet")
DEFAULT_INSTANT_CLIENT_DIR = Path("/Users/dbuser/.oracle/instant_client")


def normalize_private_key(key_text: str) -> str:
    """
    Return the key in the format expected by Thick-mode OCI IAM auth.
    """
    key_lines = []
    for line in key_text.splitlines():
        line = line.strip()
        if not line:
            continue
        if line == "-----BEGIN PRIVATE KEY-----":
            continue
        if line == "-----END PRIVATE KEY-----":
            continue
        key_lines.append(line)
    return "".join(key_lines).strip()


def read_oci_iam_token_pair(token_file: Path, key_file: Path) -> tuple[str, str]:
    """Read the OCI IAM access token and private key."""

    if not token_file.is_file():
        raise FileNotFoundError(f"Token file not found: {token_file}")

    if not key_file.is_file():
        raise FileNotFoundError(f"Private key file not found: {key_file}")

    token = token_file.read_text(encoding="utf-8").strip()
    key_text = key_file.read_text(encoding="utf-8").strip()

    if not token:
        raise ValueError(f"Token file is empty: {token_file}")

    if not key_text:
        raise ValueError(f"Private key file is empty: {key_file}")

    private_key = normalize_private_key(key_text)

    if not private_key:
        raise ValueError(
            f"Private key content was invalid after normalization: {key_file}"
        )

    return token, private_key


def get_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Execute SQL using OCI IAM authentication."
    )

    parser.add_argument(
        "--alias",
        required=True,
        metavar="DB_ALIAS",
        help="TNS alias from tnsnames.ora (for example DEVDB_PDB1_SSL_TOKEN).",
    )

    parser.add_argument(
        "--sql",
        required=True,
        metavar="SQL",
        help="SQL statement to execute. Enclose in quotes if it contains spaces.",
    )

    parser.add_argument(
        "--tns-admin",
        default="/Users/dbuser/.oracle/admin",
        help="Directory containing tnsnames.ora and optionally sqlnet.ora.",
    )

    parser.add_argument(
        "--wallet-dir",
        default=str(DEFAULT_WALLET_DIR),
        help="Directory containing the auto-login wallet (cwallet.sso).",
    )

    parser.add_argument(
        "--instant-client-dir",
        default=str(DEFAULT_INSTANT_CLIENT_DIR),
        help="Directory containing the Oracle Instant Client libraries.",
    )

    parser.add_argument(
        "--token-file",
        type=Path,
        default=DEFAULT_TOKEN_FILE,
        help=f"OCI IAM token file (default: {DEFAULT_TOKEN_FILE})",
    )

    parser.add_argument(
        "--key-file",
        type=Path,
        default=DEFAULT_KEY_FILE,
        help=f"OCI IAM private key file (default: {DEFAULT_KEY_FILE})",
    )

    return parser.parse_args()


def validate_paths(
    tns_admin: Path,
    wallet_dir: Path,
    instant_client_dir: Path,
) -> None:

    if not tns_admin.is_dir():
        raise FileNotFoundError(
            f"TNS admin directory not found: {tns_admin}"
        )

    if not wallet_dir.is_dir():
        raise FileNotFoundError(
            f"Wallet directory not found: {wallet_dir}"
        )

    if not instant_client_dir.is_dir():
        raise FileNotFoundError(
            f"Instant Client directory not found: {instant_client_dir}"
        )

    cwallet = wallet_dir / "cwallet.sso"
    if not cwallet.is_file():
        raise FileNotFoundError(
            f"Auto-login wallet not found: {cwallet}"
        )

    libclntsh = instant_client_dir / "libclntsh.dylib"
    if not libclntsh.exists():
        raise FileNotFoundError(
            f"Oracle Client library not found: {libclntsh}"
        )


def print_result_set(cursor: oracledb.Cursor) -> None:
    """Pretty-print a result set."""

    columns = [col[0] for col in cursor.description]
    print("\t".join(columns))

    print("-" * (len(columns) * 16))

    for row in cursor:
        print("\t".join("" if v is None else str(v) for v in row))


def main() -> int:

    args = get_args()

    tns_admin = Path(args.tns_admin).expanduser().resolve()
    wallet_dir = Path(args.wallet_dir).expanduser().resolve()
    instant_client_dir = Path(args.instant_client_dir).expanduser().resolve()

    try:
        validate_paths(
            tns_admin,
            wallet_dir,
            instant_client_dir,
        )
    except Exception as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 2

    try:
        oracledb.init_oracle_client(
            lib_dir=str(instant_client_dir),
            config_dir=str(tns_admin),
        )
    except oracledb.Error as exc:
        print(
            f"ERROR: Failed to initialize Oracle Client: {exc}",
            file=sys.stderr,
        )
        return 1

    try:
        token, private_key = read_oci_iam_token_pair(
            args.token_file.expanduser().resolve(),
            args.key_file.expanduser().resolve(),
        )
    except Exception as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 2

    sql = args.sql.strip().rstrip(";")

    try:
        with oracledb.connect(
            dsn=args.alias,
            access_token=(token, private_key),
            externalauth=True,
            wallet_location=str(wallet_dir),
        ) as conn:

            with conn.cursor() as cur:

                cur.execute(sql)

                if cur.description:
                    print_result_set(cur)
                else:
                    print(
                        f"Statement executed successfully. "
                        f"Rows affected: {cur.rowcount}"
                    )

    except oracledb.Error as exc:
        print(f"Oracle error: {exc}", file=sys.stderr)
        return 1

    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Here's the usage as provided by the script:

$ ./ociiam-python-thick.py --help
usage: ociiam-python-thick.py [-h] --alias DB_ALIAS --sql SQL [--tns-admin TNS_ADMIN] [--wallet-dir WALLET_DIR]
                              [--instant-client-dir INSTANT_CLIENT_DIR] [--token-file TOKEN_FILE] [--key-file KEY_FILE]

Execute SQL using OCI IAM authentication.

options:
  -h, --help            show this help message and exit
  --alias DB_ALIAS      TNS alias from tnsnames.ora (for example DEVDB_PDB1_SSL_TOKEN).
  --sql SQL             SQL statement to execute. Enclose in quotes if it contains spaces.
  --tns-admin TNS_ADMIN
                        Directory containing tnsnames.ora and optionally sqlnet.ora.
  --wallet-dir WALLET_DIR
                        Directory containing the auto-login wallet (cwallet.sso).
  --instant-client-dir INSTANT_CLIENT_DIR
                        Directory containing the Oracle Instant Client libraries.
  --token-file TOKEN_FILE
                        OCI IAM token file (default: /Users/dbuser/.oci/db-token/token)
  --key-file KEY_FILE   OCI IAM private key file (default: /Users/dbuser/.oci/db-token/oci_db_key.pem)

Here's an example flow that incorporates starting an OCI IAM session, getting an OCI IAM access token and executing the script using an existing tnsnames.ora file and client wallet.

First, initiate an OCI IAM session with the OCI command line tool.

oci session authenticate --profile-name DBUSER --tenancy-name <oci_tenancy_name> --region <oci_region> --identity-provider-name <oci_identity_domain_name> 
    Please switch to newly opened browser window to log in!
    You can also open the following URL in a web browser window to continue:
https://login.us-phoenix-1.oraclecloud.com/v1/oauth2/authorize?action=login...
Service
    Completed browser authentication process!
Config written to: /Users/dbuser/.oci/config

Next, I request an OCI IAM access token.

oci iam db-token get --profile DBUSER --auth security_token --scope urn:oracle:db::id::<oci_compartment_ocid>::*

Once authenticated and have the access token, then we can run the the python script with requisite arguments. First, lets authenticate and show the database user name:

$ ./ociiam-python-thick.py --alias DEVDB_PDB1_SSL_TOKEN --tns-admin /Users/dbuser/.oracle/admin --wallet-dir /Users/dbuser/.oracle/admin/client_wallet --instant-client-dir /Users/dbuser/.oracle/instant_client --sql "SELECT SYS_CONTEXT ('USERENV','CURRENT_USER') FROM DUAL"
SYS_CONTEXT('USERENV','CURRENT_USER')
----------------
ALLDBUSERS

Next, lets authenticate and show the authenticated user:

$ ./ociiam-python-thick.py --alias DEVDB_PDB1_SSL_TOKEN --tns-admin /Users/dbuser/.oracle/admin --wallet-dir /Users/dbuser/.oracle/admin/client_wallet --instant-client-dir /Users/dbuser/.oracle/instant_client --sql "SELECT SYS_CONTEXT ('USERENV','AUTHENTICATED_IDENTITY') FROM DUAL"
SYS_CONTEXT('USERENV','AUTHENTICATED_IDENTITY')
----------------
<oci_identity_domain_name>/dbuser@dbauthdemo.com

Next, lets authenticate and show the enterprise user:

$ ./ociiam-python-thick.py --alias DEVDB_PDB1_SSL_TOKEN --tns-admin /Users/dbuser/.oracle/admin --wallet-dir /Users/dbuser/.oracle/admin/client_wallet --instant-client-dir /Users/dbuser/.oracle/instant_client --sql "SELECT SYS_CONTEXT ('USERENV','ENTERPRISE_IDENTITY') FROM DUAL"
SYS_CONTEXT('USERENV','ENTERPRISE_IDENTITY')
----------------
<oci_user_ocid>


Thin Oracle DB Driver Client

With the thin driver client python script, I had to iterate through several prompts to refine to a usable script.  The key issue that I had to wrestle with was how connect over TLS to the Oracle AI Database server where the server's certificate is a self-signed certificate.  The short answer is that it is not possible without overriding the trust model to ignore certificate verification.  Please note that disregarding certificate verification is NOT SECURE and should not be done.  That said, my test environment did not have a Certificate Authority (CA) signed certificate. Therefore, I had to implement the override parameter (--insecure) to demonstrate successful connection and SQL execution.

Here's the resulting script

$ cat ./ociiam-python-thin.py
#!/usr/bin/env python3.14
"""
Execute SQL against Oracle Database using OCI IAM token auth in Thin mode,
with a TNS alias and optional TLS certificate verification bypass.

This version does NOT use Oracle wallets or client-side mutual TLS.

Expected locations:
- TNS admin directory: /Users/dbuser/.oracle/admin
- Token file:          /Users/dbuser/.oci/db-token/token
- Private key file:    /Users/dbuser/.oci/db-token/oci_db_key.pem

Use --insecure only for testing.
"""

from __future__ import annotations

import argparse
import ssl
import sys
from pathlib import Path

import oracledb


DEFAULT_TOKEN_FILE = Path("/Users/
dbuser/.oci/db-token/token")
DEFAULT_KEY_FILE = Path("/Users/dbuser/.oci/db-token/oci_db_key.pem")
DEFAULT_TNS_ADMIN = Path("/Users/dbuser/.oracle/admin")


def read_oci_iam_token_pair(token_file: Path, key_file: Path) -> tuple[str, str]:
    if not token_file.is_file():
        raise FileNotFoundError(f"Token file not found: {token_file}")
    if not key_file.is_file():
        raise FileNotFoundError(f"Private key file not found: {key_file}")

    token = token_file.read_text(encoding="utf-8").strip()
    private_key = key_file.read_text(encoding="utf-8").strip()

    if not token:
        raise ValueError(f"Token file is empty: {token_file}")
    if not private_key:
        raise ValueError(f"Private key file is empty: {key_file}")

    if "-----BEGIN" not in private_key or "-----END" not in private_key:
        raise ValueError(
            f"Private key file does not appear to be PEM-formatted: {key_file}"
        )

    return token, private_key


def get_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Execute SQL using OCI IAM authentication in Thin mode."
    )

    parser.add_argument(
        "--alias",
        required=True,
        metavar="DB_ALIAS",
        help="TNS alias from tnsnames.ora (for example DEVDB_PDB1_SSL_TOKEN).",
    )

    parser.add_argument(
        "--sql",
        required=True,
        metavar="SQL",
        help="SQL statement to execute. Enclose in quotes if it contains spaces.",
    )

    parser.add_argument(
        "--tns-admin",
        default=str(DEFAULT_TNS_ADMIN),
        help="Directory containing tnsnames.ora and optionally sqlnet.ora.",
    )

    parser.add_argument(
        "--trust-cert",
        type=Path,
        default=None,
        metavar="PEM_FILE",
        help=(
            "Optional PEM file containing a trusted CA certificate chain. "
            "Ignored when --insecure is used."
        ),
    )

    parser.add_argument(
        "--insecure",
        action="store_true",
        help=(
            "Disable TLS server certificate verification. "
            "FOR TESTING ONLY."
        ),
    )

    parser.add_argument(
        "--token-file",
        type=Path,
        default=DEFAULT_TOKEN_FILE,
        help=f"OCI IAM token file (default: {DEFAULT_TOKEN_FILE})",
    )

    parser.add_argument(
        "--key-file",
        type=Path,
        default=DEFAULT_KEY_FILE,
        help=f"OCI IAM private key file (default: {DEFAULT_KEY_FILE})",
    )

    return parser.parse_args()


def validate_paths(tns_admin: Path, trust_cert: Path | None) -> None:
    if not tns_admin.is_dir():
        raise FileNotFoundError(f"TNS admin directory not found: {tns_admin}")

    if trust_cert is not None and not trust_cert.is_file():
        raise FileNotFoundError(f"Trust certificate file not found: {trust_cert}")


def build_ssl_context(trust_cert: Path | None, insecure: bool) -> ssl.SSLContext:
    """
    Build the SSL context.

    insecure=True disables certificate verification and hostname verification.
    This should only be used for testing.
    """
    if insecure:
        context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE
        return context

    context = ssl.create_default_context()

    if trust_cert is not None:
        context.load_verify_locations(cafile=str(trust_cert))

    return context


def print_result_set(cursor: oracledb.Cursor) -> None:
    columns = [col[0] for col in cursor.description]
    print("\t".join(columns))
    print("-" * max(8, len(columns) * 16))
    for row in cursor:
        print("\t".join("" if v is None else str(v) for v in row))


def main() -> int:
    args = get_args()

    tns_admin = Path(args.tns_admin).expanduser().resolve()
    trust_cert = args.trust_cert.expanduser().resolve() if args.trust_cert else None

    try:
        validate_paths(tns_admin, trust_cert)
    except Exception as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 2

    if args.insecure:
        print(
            "WARNING: TLS certificate verification is DISABLED. "
            "This should only be used for testing.",
            file=sys.stderr,
        )

    try:
        token, private_key = read_oci_iam_token_pair(
            args.token_file.expanduser().resolve(),
            args.key_file.expanduser().resolve(),
        )
    except Exception as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 2

    # Configure tnsnames.ora lookup for Thin mode.
    oracledb.defaults.config_dir = str(tns_admin)

    ssl_context = build_ssl_context(trust_cert, args.insecure)

    sql = args.sql.strip().rstrip(";")

    try:
        with oracledb.connect(
            dsn=args.alias,
            ssl_context=ssl_context,
            access_token=(token, private_key),
            externalauth=True,
        ) as conn:
            with conn.cursor() as cur:
                cur.execute(sql)

                if cur.description:
                    print_result_set(cur)
                else:
                    print(
                        f"Statement executed successfully. Rows affected: {cur.rowcount}"
                    )

    except oracledb.Error as exc:
        print(f"Oracle error: {exc}", file=sys.stderr)
        return 1

    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Here's the usage as provided by the script:

$ ./ociiam-python-thin.py --help
usage: ociiam-python-thin.py [-h] --alias DB_ALIAS --sql SQL [--tns-admin TNS_ADMIN] [--trust-cert PEM_FILE] [--insecure]
                             [--token-file TOKEN_FILE] [--key-file KEY_FILE]

Execute SQL using OCI IAM authentication in Thin mode.

options:
  -h, --help            show this help message and exit
  --alias DB_ALIAS      TNS alias from tnsnames.ora (for example DEVDB_PDB1_SSL_TOKEN).
  --sql SQL             SQL statement to execute. Enclose in quotes if it contains spaces.
  --tns-admin TNS_ADMIN
                        Directory containing tnsnames.ora and optionally sqlnet.ora.
  --trust-cert PEM_FILE
                        Optional PEM file containing a trusted CA certificate chain. Ignored when --insecure is used.
  --insecure            Disable TLS server certificate verification. FOR TESTING ONLY.
  --token-file TOKEN_FILE
                        OCI IAM token file (default: /Users/dbuser/.oci/db-token/token)
  --key-file KEY_FILE   OCI IAM private key file (default: /Users/dbuser/.oci/db-token/oci_db_key.pem)


Here's an example flow that incorporates starting an OCI IAM session, getting an OCI IAM access token and executing the script using an existing tnsnames.ora file and client wallet.

First, initiate an OCI IAM session with the OCI command line tool.

oci session authenticate --profile-name DBUSER --tenancy-name <oci_tenancy_name> --region <oci_region> --identity-provider-name <oci_identity_domain_name> 
    Please switch to newly opened browser window to log in!
    You can also open the following URL in a web browser window to continue:
https://login.us-phoenix-1.oraclecloud.com/v1/oauth2/authorize?action=login...
Service
    Completed browser authentication process!
Config written to: /Users/dbuser/.oci/config

Next, I request an OCI IAM access token.

oci iam db-token get --profile DBUSER --auth security_token --scope urn:oracle:db::id::<oci_compartment_ocid>::*

Once in the python3.14 virtual environment, authenticated with oci iam, and have the OCI IAM access token, then we can run the the python script with requisite arguments.

First, lets authenticate and show the database user name:

$ ./ociiam-python-thin.py --insecure --alias DEVDB_PDB1_SSL_TOKEN --tns-admin /Users/dbuser/.oracle/admin --sql "SELECT SYS_CONTEXT ('USERENV','CURRENT_USER') FROM DUAL"
WARNING: TLS certificate verification is DISABLED. This should only be used for testing.
SYS_CONTEXT('USERENV','CURRENT_USER')
----------------
ALLDBUSERS

Next, lets authenticate and show the authenticated user:

$ ./ociiam-python-thin.py --insecure --alias DEVDB_PDB1_SSL_TOKEN --tns-admin /Users/dbuser/.oracle/admin --sql "SELECT SYS_CONTEXT ('USERENV','AUTHENTICATED_IDENTITY') FROM DUAL"
WARNING: TLS certificate verification is DISABLED. This should only be used for testing.
SYS_CONTEXT('USERENV','AUTHENTICATED_IDENTITY')
----------------
<oci_identity_domain_name>/dbuser@dbauthdemo.com

Next, lets authenticate and show the enterprise user:

$ ./ociiam-python-thin.py --insecure --alias DEVDB_PDB1_SSL_TOKEN --tns-admin /Users/dbuser/.oracle/admin --sql "SELECT SYS_CONTEXT ('USERENV','ENTERPRISE_IDENTITY') FROM DUAL"
WARNING: TLS certificate verification is DISABLED. This should only be used for testing.
SYS_CONTEXT('USERENV','ENTERPRISE_IDENTITY')
----------------
<oci_user_ocid>


Thanks for going along this AI Code Generation journey with me.

I hope that you found this post informative and beneficial.

Blessings