BigFix Integration

Overview

Since BigFix (desktop configuration management) is well established and already has an agent presence on most Yale desktops, it was decided that it would be better to integrate SN CMDB with BigFix instead of using SN Discovery (WMI). To do this, we are leveraging the "Import Set" feature of SN, which allows us to load serialized CMDB data exports from another CMDB (in this case, BigFix).

Integration Architecture

Elvin Torres (Desktop Support) is maintaining a small piece of middleware that pulls information from BigFix's SOAP API, serializes it as XML, and re-exposes it to SN via HTTP. SN is configured with a few things:

  • a "Data Source" pointing to the URL where the XML can be downloaded
  • a "Scheduled Import" which periodically downloads the XML
  • a "Transform Map" which translates the XML into CMDB database operations. For this integration, there are two things to configure:
    • "Field Maps"--rules for one-to-one mapping of XML element contents to CMDB CI columns
    • "Transform Scripts"--logic for one-to-many mapping of components to related CMDB tables

In SN, Import Sets key off of one or more fields you identify, either via the "coalesce" switch in the Field Map or via Transform Script logic.

Transform Map Details

The base table class for this integration is the "Computer" class, or cmdb_ci_computer. This class extends "cmdb_ci" and includes many useful columns you'd expect for a PC. The field maps grab attributes from the XML and map them onto columns in this table, such as OS, name, asset owner, asset tag, serial, etc.

Components (like network adapters, disks, RAM, etc) generally are represented by rows in separate CI tables, e.g., network adapters are in "cmdb_ci_network_adapter", which contains fields more suitable for that type of CI. The Transform Script(s) include logic for inserting/updating components and associating them with their parent computer CI; this is accomplished by setting the "cmdb_ci" field in the component record to be equal to the "sys_id" value of the parent CI. The sys_id is a globally unique identifier.

Here is an example Transform Script for parsing component IP addresses and for each, inserting/updating a corresponding CI which references the parent computer. This script is of the "onAfter" type, which means it runs after the parent CI record is inserted or updated... this way, we have a sys_id to point back to.

// 
// 2011/11/29 - bw - Iterate through component XML branches and add appropriate references 
//                   in component CI tables (network interfaces, disks, etc)


//========================================//
// ipAddresses -> cmdb_ci_network_adapter //
//========================================//

//
// Load the ipAddresses element content into an object
var xmldoc = new XMLDocument(source.u_ipaddresses);

//
// Get a list of nodes to iterate, count numnodes
var nodelist = xmldoc.getNodes("//ipAddresses/*");
var numnodes = nodelist.getLength();

//
// Iterate. For each:
//   * grab the ip
//   * coalesce on network interface table (cmdb_ci == target.sys_id AND ip == ip_address)
//   * insert/update network interface record
var i=0; var ip = ""; var sgr = "";
for (i=0;i<=numnodes;i++) {
    // grab the IP
    ip = nodelist.item(i).getLastChild().getNodeValue();  

    // select & coalesce
    sgr = new GlideRecord("cmdb_ci_network_adapter");
    sgr.addQuery("cmdb_ci", target.sys_id);
    sgr.addQuery("ip_address", ip); // really should be name, not ip.
    sgr.query();
    if ( sgr.getRowCount() == 0 ) {
        // insert
        sgr.initialize();
        sgr.ip_address = ip;
        sgr.cmdb_ci = target.sys_id;
        sid = sgr.insert();
        gs.print("inserted " + ip + " to " + sid);
    } else {
        // update (this assumes only 1 row matched, so next row wins.
        sgr.next();
        sgr.ip_address = ip;
        sid = sgr.update();
        gs.print("inserted " + ip + " to " + sid);
    }   
}