The Zurich release has arrived! Interested in new features and functionalities? Click here for more

Creating a relationship using After Script via IntegrationHub ETL

ManasiC
Tera Contributor

I am trying to create a relationship between Class A CI and Class B CI via data received from source. I have created a JDBC connection by passing a SQL query to get the data from source database.  I've also created a Robust transformer to transform the data received from source database to the target table. I am receiving Class B CI data in a single column, comma separated in string format. This field is not mapped directly to any target field in the target table. I want to create an After script that would check the data in the source column irrespective of the number of CIs passed in the source field and check if it has an existing relationship with Class A CI, if Yes, then no action to be taken, if No, then create a relationship between Class A CI and Class B CI.

PFB some information to help with the case:
Source field "u_endpoint" contains values like ABC1234, FGH3456, etc. This is not an exact name of Class B CI so I'll need to query Class B as "NameLIKE + u_endpoint".

I have tried a few approaches as well, I've tried using script operation by splitting the data in the source field and using Multiple Input/Output script to get the result stored in the array, but this is not a feasible solution as I might have more than 2 or 3 CIs getting passed in my source column.
I have also tried using Table Lookup but the problem is same as above.

Hence, I am trying to use an After Script that checks the Class B CIs passed in source column (irrespective of the number of CIs) and creates a relationship with class A CI if it's not already created in cmdb_rel_ci table.

It would help if I could get some inputs as well as some references on how I can script this logic and pass my source column in ETL After Script (attaching the script format for reference) as I've never tried it before and couldn't find any references online.IntegrationHub After script.png

4 REPLIES 4

AJ-TechTrek
Giga Sage
Giga Sage

Hi @ManasiC ,

 

As per my understanding , you’re basically doing a JDBC ETL → Robust Transformer → ETL After Script that needs to

 

1. Take your source u_endpoint field (comma-separated IDs/partial names of Class B CIs).
2. Resolve them to actual Class B CIs using a NameLIKE match.
3. Check if a relationship between Class A (current CI) and each Class B CI exists in cmdb_rel_ci.
4. Create it if it doesn’t.


Below is a pattern you can use in your ETL After Script for this.
I’ll also explain where to plug it in so you can handle variable-length comma-separated values without hard-coding.

 

Example ETL After Script - I am not much good in Scripting side but this might provide you an insight -


(function transformEntry(source, target, map, log, isUpdate) {
try {
var classA_sys_id = target.sys_id; // CI just created/updated by ETL
var endpointString = source.u_endpoint || '';
if (!endpointString) return;

// Split and trim comma-separated list
var endpoints = endpointString.split(',').map(function(ep) {
return ep.trim();
}).filter(Boolean); // remove blanks

endpoints.forEach(function(endpoint) {
// Find matching Class B CI (NameLIKE match)
var classB = new GlideRecord('cmdb_ci_class_b'); // replace with actual table name
classB.addQuery('name', 'LIKE', endpoint);
classB.query();

while (classB.next()) {
// Check if relationship already exists
var rel = new GlideRecord('cmdb_rel_ci');
rel.addQuery('parent', classA_sys_id); // Class A
rel.addQuery('child', classB.sys_id); // Class B
rel.query();

if (!rel.hasNext()) {
// Create new relationship
var newRel = new GlideRecord('cmdb_rel_ci');
newRel.initialize();
newRel.parent = classA_sys_id;
newRel.child = classB.sys_id;
newRel.type = 'Depends on::Used by'; // replace with your relationship type sys_id
newRel.insert();

log.info('Relationship created between Class A: ' + classA_sys_id +
' and Class B: ' + classB.sys_id);
} else {
log.info('Relationship already exists for: ' + endpoint);
}
}
});

} catch (ex) {
log.error('Error in After Script: ' + ex.message);
}
})(source, target, map, log, isUpdate);

 

Key Points for Your Case
* source → Holds the data coming from your JDBC source record.
* target → Holds the CI just created/updated in ServiceNow by ETL mapping.
* u_endpoint → Must be referenced from source because it’s not mapped to a target field.
* Dynamic handling → Uses .split(',') so you can handle 2, 3, or 50 endpoints without changes.
* LIKE Search → Replace 'cmdb_ci_class_b' with the real table for Class B CIs.
* Relationship Type → Replace 'Depends on::Used by' with the actual relationship type sys_id you want.
* Performance Note → If your endpoints list or CMDB is large, you may want to cache results in a JS object to avoid duplicate queries in one run.

 

Where to Put This
* In your ETL Definition → Transform Map → After Script.
* Ensure your robust transformer mapping does not map u_endpoint to any field, so you can fully control it in the script.
* Test in sub-prod with a few rows first — relationship creation is immediate and irreversible without cleanup.

 

Why This Works Better Than Table Lookup or Multi-IO Scripts
* No fixed array length → Works with unlimited comma-separated values.
* Direct CMDB Relationship creation → Avoids intermediate fields.
* Single pass → Reads u_endpoint once, processes all values.

 

Please appreciate the efforts of community contributors by marking appropriate response as Mark my Answer Helpful or Accept Solution this may help other community users to follow correct solution in future.
 

Thank You
AJ - TechTrek with AJ - ITOM Trainer
LinkedIn:- https://www.linkedin.com/in/ajay-kumar-66a91385/
YouTube:- https://www.youtube.com/@learnitomwithaj
Topmate:- https://topmate.io/aj_techtrekwithaj (Connect for 1-1 Session)
ServiceNow Community MVP 2025

Hi @AJ-TechTrek,

Thank you for your response. Yes, you’ve understood correctly what I’m trying to achieve. However, the script you’ve shared appears to be a standard transform script. In the ETL After script, we don’t have the (function transformEntry(source, target, map, log, isUpdate) you mentioned — instead, it follows the format (function(ireOutput, ireInput, runId).

My challenge is that I’m unable to pass the u_endpoint source column in this script, as it isn’t returned in any of the OOB variables provided. Without it, I can’t verify the relationship between my Class A and each Class B CI.

Could you advise on how I can pass my source column in this script? Any guidance would be greatly appreciated. Thank you for your time and expertise.

Hi @AJ-TechTrek ,

Thank you for your response. Yes, you've understood correctly what I'm trying to achieve. However, the script you've shared appears to be a standard transform script. In the ETL After script, we don't have (function transformEntry(source, target, map, log, isUpdate) you mentioned - instead, it follows the format (function (ireOutput, ireInput, runId).

 

My challenge is that I'm unable to pass the u_endpoint source column in this script, as it isn't returned in any of the OOB variables provided. Without it, I can't verify the relationship between my Class A CI and each Class B CI.

Could you advise on how I can pass my source column in this script? Any guidance would be greatly appreciated. Thank you for your time and expertise.

Hi @ManasiC ,

 

Got it — the main issue is that in ETL After Scripts


(function (ireOutput, ireInput, runId) { ... })
 you don’t have direct access to the source transform map object (source.u_endpoint) like you do in a standard transform script.
That’s why your u_endpoint field isn’t available in ireOutput or ireInput by default.

 

1. Why the field is missing
- The ETL After Script runs after IRE has processed the CI data.
- ireOutput contains the CMDB CI record(s) created/updated by IRE.
- ireInput contains only the normalized IRE payload — not the raw ETL source columns unless you **explicitly map them in the ETL configuration.


2. How to get u_endpoint into your After Script
To pass u_endpoint from the JDBC source to your After Script:
1. Add a temporary pass-through field in the IRE payload:
- In your ETL transform map, map u_endpoint → an extension field on your Class A CI table (e.g., u_temp_endpoint).
- This field doesn’t have to be visible in the form — it’s just a carrier.
2. In your After Script, read u_temp_endpoint from the ireOutput GlideRecord.
3. Process it into relationships and then clear it if you don’t want to store it.

 

3. Example After Script using pass-through


(function (ireOutput, ireInput, runId) {
try {
while (ireOutput.next()) {
var classA_sys_id = ireOutput.sys_id + '';

// Get endpoint list from the temp field mapped in ETL
var endpointString = (ireOutput.u_temp_endpoint || '').trim();
if (!endpointString)
continue;

var endpoints = endpointString.split(',')
.map(function(ep) { return ep.trim(); })
.filter(Boolean);

endpoints.forEach(function(endpoint) {
// Find matching Class B CI
var classB = new GlideRecord('cmdb_ci_class_b'); // update table name
classB.addQuery('name', 'LIKE', endpoint);
classB.query();

while (classB.next()) {
// Check existing relationship
var rel = new GlideRecord('cmdb_rel_ci');
rel.addQuery('parent', classA_sys_id);
rel.addQuery('child', classB.sys_id);
rel.query();

if (!rel.hasNext()) {
var newRel = new GlideRecord('cmdb_rel_ci');
newRel.initialize();
newRel.parent = classA_sys_id;
newRel.child = classB.sys_id;
newRel.type = 'Depends on::Used by'; // use your rel type
newRel.insert();
}
}
});

// Optional: Clear the temp field so it doesn't persist
ireOutput.u_temp_endpoint = '';
ireOutput.update();
}
} catch (ex) {
gs.error('Error in After Script: ' + ex.message);
}
})(ireOutput, ireInput, runId);

 

4. Key points as per my understanding
* You cannot directly access source in After Scripts — you must pass data via a mapped field or an IRE Extension Point.
* Using a temp field keeps the payload intact and makes After Script logic possible.
* If you want to avoid storing the field, you can delete it after relationship creation.

 

Please appreciate the efforts of community contributors by marking appropriate response as Mark my Answer Helpful or Accept Solution this may help other community users to follow correct solution in future.
 

Thank You
AJ - TechTrek with AJ - ITOM Trainer
LinkedIn:- https://www.linkedin.com/in/ajay-kumar-66a91385/
YouTube:- https://www.youtube.com/@learnitomwithaj
Topmate:- https://topmate.io/aj_techtrekwithaj (Connect for 1-1 Session)
ServiceNow Community MVP 2025