The automation is a chain of three small scripts, triggered by the Cloud Manager's post-clone hook. All hostnames below are anonymized; adapt paths and names to your environment.
Hook in the post-clone script (Cloud Manager host)
Our generic post-clone script simply calls the Asserter deployment for the environments that have SSO enabled:
# EBS Asserter; only for DEV and QAT
if [ "$SID" = "DEV" ] || [ "$SID" = "QAT" ]; then
deploy_ebs_asserter.sh $IP_APPS $SID
fi
deploy_ebs_asserter.sh (Cloud Manager host)
The orchestrator: create a fresh DBC file on the apps tier, ship it to the Docker host, and trigger the redeployment there.
#!/bin/bash
IP_APPS=$1
SID=$2
ssh $IP_APPS "~/createEbsAsserterDBC.sh"
scp $IP_APPS:~/${SID}_EBSASSERTER.EXAMPLE.COM.dbc .
scp ${SID}_EBSASSERTER.EXAMPLE.COM.dbc opc@dockerhost:/tmp/
rm ${SID}_EBSASSERTER.EXAMPLE.COM.dbc
ssh opc@dockerhost "sudo /root/ebs_asserter/update_environment.sh $SID"
createEbsAsserterDBC.sh (EBS apps tier)
Regenerates the DBC file for the Asserter node, then fixes the database hostname. The clone source is production, so the freshly generated file still references the production DB host; a sed replaces it with the host of the cloned environment (derived from the SID):
#!/bin/bash
source /u01/install/APPS/EBSapps.env run
java oracle.apps.fnd.security.AdminDesktop apps/$APPS_PWD CREATE \
NODE_NAME=ebsasserter.example.com DBC=$FND_SECURE/$TWO_TASK.dbc
cp ${TWO_TASK}_EBSASSERTER.EXAMPLE.COM.dbc \ ${TWO_TASK}_EBSASSERTER.EXAMPLE.COM.dbc.org
sed -i -e 's/prodebsdb/'${TWO_TASK,,}'ebsdb/g' \ ${TWO_TASK}_EBSASSERTER.EXAMPLE.COM.dbc
update_environment.sh (Docker host)
The heart of the automation. It extracts the new APPL_SERVER_ID from the DBC file, patches bridge.properties, rebuilds the WAR from a pristine copy, and runs the exact deployment sequence from Part 2, health checks included:
#!/bin/bash
SID=$1
SID_LOWER=${SID,,}
COMPOSE_DIR=/root/ebs_asserter/${SID_LOWER}/
CONTAINER_HOME=/root/ebs_asserter/home_oracle_${SID_LOWER}
CONTAINER_NAME=ebs_asserter_${SID_LOWER}
echo "Patching environment $SID"
cd $COMPOSE_DIR
docker compose down
# 1. Move the fresh DBC file into place
DBC_FILE=${CONTAINER_HOME}/${SID}_EBSASSERTER.EXAMPLE.COM.dbc
mv /tmp/${SID}_EBSASSERTER.EXAMPLE.COM.dbc $DBC_FILE
chmod 644 $DBC_FILE
# 2. Extract the new APPL_SERVER_ID and patch bridge.properties
NEW_APPL_SERVER_ID=$(sed -n 's/^APPL_SERVER_ID=//p' "$DBC_FILE" | tr -d '\r' | head -n1)
echo "NEW_APPL_SERVER_ID: $NEW_APPL_SERVER_ID"
sed -i "s/^app\.serverid=.*/app.serverid=${NEW_APPL_SERVER_ID}/" \
"${CONTAINER_HOME}/WEB-INF/bridge.properties"
# 3. Rebuild ebs.war from a pristine copy
cd $CONTAINER_HOME
cp ../software_ebsasserter/build/libs/ebs.war .
zip -u ebs.war WEB-INF/*
# 4. Start the container and wait until healthy
cd $COMPOSE_DIR
docker compose up -d --wait
until [ "$(docker inspect -f '{{.State.Health.Status}}' ${CONTAINER_NAME})" = "healthy" ]; do
echo "waiting for ebs_asserter container ${CONTAINER_NAME}..."
sleep 2
done
sleep 5
# 5. fndext.jar into the domain lib, restart, wait again
docker exec $CONTAINER_NAME cp /home/oracle/ebs_asserter/fndext-2.0.9.jar \
/u01/oracle/user_projects/domains/asserter_domain/lib
docker restart $CONTAINER_NAME
until [ "$(docker inspect -f '{{.State.Health.Status}}' ${CONTAINER_NAME})" = "healthy" ]; do
echo "waiting for ebs_asserter container ${CONTAINER_NAME}..."
sleep 2
done
sleep 5
# 6. Deploy datasource and Asserter via autodeploy
docker exec $CONTAINER_NAME cp /home/oracle/ebs_asserter/visionDS-jdbc-fndext.xml \ /u01/oracle/user_projects/domains/asserter_domain/autodeploy/visionDS-jdbc.xml
docker exec $CONTAINER_NAME cp /home/oracle/ebs_asserter/ebs.war \
/u01/oracle/user_projects/domains/asserter_domain/autodeploy/
A few design decisions worth pointing out:
The WAR is always rebuilt from a pristine copy of the downloaded Asserter, with bridge.properties maintained outside the archive and merged in via zip -u. The script is idempotent; running it twice does no harm.
docker compose down first, then up. The container is disposable by design (Part 1 pays off here): no state worth preserving, so a clean start beats patching a running domain.
Health checks gate every step. The autodeploy directory only works reliably once the AdminServer is fully up, hence the wait loops after each start. docker compose up -d --wait covers the first start, the loop covers the restart after the fndext.jar copy.
No WebLogic console, no wlst, no manual step. The same mechanism deploys a brand-new environment: create the home directory, drop in wallet and compose file, run the script.
Finally post cloning the relevant profile options have to be adjusted:
declare
stat boolean;
m_node_id fnd_nodes.node_id%type := 0;
l_tmp_url varchar2(100);
begin
SELECT vals.profile_option_value||'/ebs/', node_id
into l_tmp_url, m_node_id
FROM
fnd_profile_option_values vals, fnd_nodes nodes
WHERE
vals.profile_option_id = 4177
AND vals.level_id = 10007
and vals.level_value2=nodes.node_id
and nodes.node_name like '%APP01'
;
stat:=fnd_profile.save('APPS_AUTH_AGENT',
l_tmp_url,
'SERVER',
m_node_id);
stat:=fnd_profile.save('APPS_SSO',
'SSWA_SSO',
'SERVER',
m_node_id);
stat:=fnd_profile.save('APPS_SSO_LOCAL_LOGIN',
'SSO',
'SITE');
exception when no_data_found then
null;
end;
Since this automation went live, cloned environments come back with working SSO before anyone has had their first coffee. The clone notification mail arrives, the environment is ready, and nobody thinks about APPL_SERVER_ID anymore, which is exactly how it should be.