CI Lifecycle Management - How To?

stevejarman
Giga Guru

Hi - I'm hoping someone might be able to share some Best Practice information with me regarding CI Lifecycle Management, and specifically, how to correctly Retire CIs (mainly focusing on Servers).

I've found quite a few examples of people using scripts to set Retired operational status on CIs not discovered within a specific number of days (e.g. not discovered in 14 days? Set operational status = Retired). There's even an example of this by ServiceNow themselves within one of the CMDB training labs.

I've also found variations of that where they will also delete cmdb_rel_ci records where the parent or child is the CI that is being set to Retired.

And, I've also been reading up on the CI Lifecycle Management module within ServiceNow, but have been unable to find any documentation specific to the retirement process, so I'm not sure if this is the something this module handles or not.

I've written a fairly comprehensive script to handle retirement and also optionally delete relationships, but before I go ahead and implement something like this, I'd like to confirm 100% that this is the correct approach.

Can anyone who has proven/definitive knowledge on this subject please share your thoughts?

11 REPLIES 11

christianmalone
ServiceNow Employee
ServiceNow Employee
I’m partial to using the cmdb compliance modules to right my policies and assign my tasks. This allows me to fail an audit for server that is in production but hasn’t been seen for 30days- the task gets assigned to the owner of the CI and then provide them a UI button (ACL for only show to CI owners or cmdb admins) which retires CI that UI button or list action runs your script so you get the best of both worlds.

I like the idea of it being a manual process with the UI Action moving forward. In my case, I need some sort of bulk run initially as the rules need to be applied to thousands of CIs that haven't ever been processed.

One thing about your suggestion - it's custom. So I guess that's one vote for there not being a clear OOTB method of doing this?

doug_schulze
ServiceNow Employee
ServiceNow Employee

We did a Knowledge lab around this very topic, take a look at it here.

Thanks Doug. That definitely seems to answer the question for me re whether or not there is something OOTB in ServiceNow where I can simply create a rules which says "If CI not seen in 60 days, retire, and cascade that logic to everything related".

So now, I feel more confident doing this with my scripted version, however I have a couple of doubts...

The first is that I had just added a section to my script to delete cmdb_ci_rel records relating to the CI that's being RETIRED. Looking at the UI Action in the lab (page 22), it looks like if I want to do things right, I need to really adopt that entire UI Action logic in as well - i.e. all the additional tables.

The second thing is the mention of DELETING cmdb_ci_appl entires too. I hadn't even considered that. Do you think that's a recommendation that should be followed?

Below is what I've been toying with in DEV. I opted for specifying classes vs hitting all of cmdb_ci_hardware at once. I also added a second check for reversing the flags just in case they somehow don't get reverse automatically - I'm really not sure if this is necessary, but couldn't see the harm in doing it.

I'd be interested to hear any and all thoughts on this topic - especially if anyone else has implemented something themselves. And I'm curious - of people using CMDB, who aren't doing any of this - how are they dealing with retiring/decommissioning? Or are you just not dealing with it at all?

var retireThresholdDays = 60;
var unretireThresholdDays = 3;

performCleanUp("cmdb_ci_win_server");
performCleanUp("cmdb_ci_linux_server");
//performCleanUp("cmdb_ci_solaris_server");
//performCleanUp("cmdb_ci_hpux_server");

function performCleanUp(_class) {
    // FIND CIs THAT ARE:
    // OPERATIONAL
    // NEVER DISCOVERED, AND NOT UPDATED IN THE PAST x DAYS
    // OR
    // DISCOVERED, BUT NOT WITHIN THE LAST x DAYS
    var rsDisable = new GlideRecord(_class);
    rsDisable.addEncodedQuery("operational_status=1^last_discoveredISEMPTY^sys_updated_onRELATIVELT@dayofweek@ago@" + retireThresholdDays + "^NQoperational_status=1^last_discoveredRELATIVELT@dayofweek@ago@" + retireThresholdDays);
    rsDisable.query();

    gs.log("Scheduled job [(AC3) Retire/Unretire CIs] retiring " + rsDisable.getRowCount() + " " + _class + " CI(s).");

    while (rsDisable.next()) {
        rsDisable.operational_status = 6; // RETIRED

        if (rsDisable.isValidField("u_status")) {
            rsDisable.u_status = "Decommissioned";
        }

        rsDisable.update();

        // OPTIONALLY, LET'S DELETE ANY RELATIONSHIPS FOR THIS CI
        //DeleteRelationships(rsDisable.sys_id.toString());
    }

    // FIND CIs THAT ARE:
    // NOT OPERATIONAL
    // DISCOVERED WITHIN THE LAST x DAYS
    var rsEnable = new GlideRecord(_class);
    rsEnable.addEncodedQuery("last_discoveredRELATIVEGT@dayofweek@ago@" + unretireThresholdDays + "^discovery_source=ServiceNow^operational_status!=1");
    rsEnable.query();

    gs.log("Scheduled job [(AC3) Retire/Unretire CIs] unretiring " + rsEnable.getRowCount() + " " + _class + " CI(s).");

    while (rsEnable.next()) {
        rsEnable.operational_status = 1; // OPERATIONAL

        if (rsEnable.isValidField("u_status")) {
            rsEnable.u_status = "Commissioned";
        }

        rsEnable.update();
    }
}

function DeleteRelationships(_ci_sysid) {
    var rsRelationships = new GlideRecord("cmdb_rel_ci");
    rs.addEncodedQuery("parent=" + _ci_sysid + "^ORchild=" + _ci_sysid);
    rs.query();

    while (rs.next()) {
        rs.deleteRecord();
    }
}