SAMP - Subscription licenses and maintenance renewals

webdev123
Kilo Contributor

What's the best way / practice to track licensing and maintenance of subscription software

 

We have subscription based licenses that renew on a yearly basis.  We are creating a PO for the initial purchase, and beyond that are unsure which contract type to use - maintenance or subscription?

Related question - is there a way to tie the entitlements back to the contract in such a way that it will automatically update the subscription end dates on the entitlements, or do we need to set up workflow to update as the contract gets renewed?

 

We're having trouble finding documentation that states best practice, so this is what I've come up with on my own, playing in our dev instance.

 

1. Create PO, including PO line for purchase of software

2. Create contract for the maintenance (separate line item on the PO. I'm unsure if this should be reflected in step #1, or if I should be using contract type of maintenance or subscription)

3. Create receiving slip and lines

4. Update entitlement to reflect allocated users, subscription based license and associate with the contract. (Note: I'm unable to associate the entitlements to a contract unless type is "subscription" - is this by design?)

 

Thanks in advance.

 

 

5 REPLIES 5

cody_wolf
ServiceNow Employee
ServiceNow Employee

My recommendation:

  1. Buy SAM-Professional
  2. Setup the out of box integrations into Adobe/Microsoft portals
  3. Integration populates the <Software Subscriptions> table.
  4. Create <SW Entitlements> to track the purchased rights.
  5. Set the <Start/End Dates>, <License Metric>, <License Duration>, <Software Model>, <Active Rights>, etc.

Below is a snip of what a  "Subscription" SW Entitlement looks like.

find_real_file.png

 

 

 

https://docs.servicenow.com/bundle/madrid-software-asset-management/page/product/software-asset-mana...

 The <Financial> tab on the SW Entitlement has fields for tracking procurement information.

Your compliance position will count (Total # of subscriptions) vs. (Total # of active rights).

Below is an example of an Adobe compliance position for "DC Professional" User Subscriptions.

find_real_file.png

 

 

The SW Entitlements can roll up to a Contract.    An example would be the subscription SW Entitlements roll up to the Master Software Agreement (Contract) you have signed with Microsoft.

Good New:  We'll be adding more capabilities in the future around Contracts and SAM.  : )

For now...

You could use workflows to notify you when Contract XYZ is about to expire.    

Report to show you list of SW Entitlements purchased under Contract XYZ

OLD SW Entitlements would expire when their <End Date> is reached.

You would ADD a new batch of SW Entitlements to reflect Year 2

Link the new SW Entitlements to Contract XYZ

If you leverage our Procurement plugins, you could <Request> and receive against the <PO> as a way to auto create the SW Entitlement.

 

Hope this helps! 

 

Rohit Lobo
ServiceNow Employee
ServiceNow Employee

Lots of good information on Cody's post above. In addition to capabilities in Contracts and SAM, we're also going to add more capabilities on the license maintenance. We'd love to get your (Webster Bank) requirements on license maintenance. Let us know if you'd like to participate and I can contact you directly outside of the forum.

In the meantime, here is some more documentation I found on contracts that speaks to your use cases. Perhaps you already found this.

Creating a contract (has specific info on Maintenance and Software License Contracts)

Creating a software maintenance contract example  

Mike Condon
Tera Expert

I realize this is 2 years old, but its an issue that we are dealing with.  The biggest hang up I have around software subscriptions revolves around allocations.  If I've created a Subscription Entitlement for 100 users for something. Then the next year I create a new Entitlement for 100 users, then I have to find a way to re-allocate all 100 users to the new subscription entitlement.

There needs to be a simple method to move the allocations between entitlements since I'm going to be creating a new entitlement each year.

Any suggestions?

Thanks!

Mike, Our Asset Manager Lisa asked me to share our solution to this problem.

 

We created a UI Action on the alm_license to move allocations to a new entitlement. This UI Action calls a UI Page into a dialog window for you to select the new entitlement to move the allocations to. The UI page then uses AJAX to call a script include to do the work of moving the allocations from one entitlement to the new one.

 

UI Action:

Name: Move allocations

Client: true (checked)

Onclick: allocationDialog()

Script:

function allocationDialog() {
//Get the values to pass into the dialog
var entitlement = g_form.getUniqueValue();

//Initialize and open the Dialog Window
var dialog = new GlideDialogWindow("pick_new_entitlement"); //Render the dialog containing the UI Page 'pick_new_entitlement'
dialog.setTitle("Pick entitlement you want to move users to"); //Set the dialog title
dialog.setPreference("current_entitlement", entitlement); //Pass in entitlement record for use in the dialog
dialog.setSize(500, 900);
dialog.removeCloseDecoration();
dialog.render(); //Open the dialog
}

 

UI Page:

Name: pick_new_entitlement

HTML/XML:

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
    <g:ui_form>
        <g:evaluate var="jvar_current_entitlement" expression="RP.getWindowProperties().get('current_entitlement')" />
        <input type="hidden" id="cancelled" name="cancelled" value="false" />
        <input type="hidden" id="oldEntitlement" name="oldEntitlement" value="${jvar_current_entitlement}" />
        <table style="padding-top:20px;">
            <tr>
                <td id="label.entitlement" class="label" nowrap="true" height="23px" type="string" choice="0"><span
                        id="status." class="mandatory label_description" mandatory="true"
                        oclass="mandatory">$[SP]</span><label for="newEntitlement" onclick=""
                        dir="">Entitlement:</label></td>
                <td nowrap="true">
                    <g:ui_reference name="newEntitlement" id="newEntitlement" table="alm_license" query="active=true"
                        completer="AJAXTableCompleter" columns="install_status;allocations_available;purchase_date" />
                </td>
            </tr>
        </table>
        <table width="100%" cellpadding="0" cellspacing="0">
            <tr>
                <td align="left" nowrap="true"><br />
                    <g:dialog_buttons_ok_cancel ok="return validateForm();" cancel="return onCancel();" />
                </td>
            </tr>
        </table>
    </g:ui_form>
</j:jelly>

Client script:

function onCancel() {
	var c = gel('cancelled');
	c.value = "true";
	GlideDialogWindow.get().destroy();
	return false;
}

function validateForm() {
	if (gel('newEntitlement').value == '') {
		alert("${JS:gs.getMessage('Please select an entitlement')}");
		return false;
	} else {
		var ga = new GlideAjax('MoveUserAllocations');
		ga.addParam('sysparm_name', 'moveAll');
		ga.addParam('sysparm_olde', gel('oldEntitlement').value);
		ga.addParam('sysparm_newe', gel('newEntitlement').value);
		ga.getXML(getMoveAnswer);
		return true;
	}
}

function getMoveAnswer(response) {
	var answer = response.responseXML.documentElement.getAttribute("answer");
	//alert(answer.toString());
	GlideDialogWindow.get().destroy();
}

Processing script:

response.sendRedirect("alm_license.do?sys_id=" + oldEntitlement);

 

 

Script Include:

Name: MoveUserAllocations

Client callable: true (checked)

Script:

var MoveUserAllocations = Class.create();
MoveUserAllocations.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	moveAll: function() {

		var olde = this.getParameter('sysparm_olde');
		//gs.log('olde:' + olde,'Entitlement Allocation Move');
		var newe = this.getParameter('sysparm_newe');
		//gs.log('newe:' + newe,'Entitlement Allocation Move');
		var users = [];

		var nl = new GlideRecord('alm_license');
		nl.get(newe);
		var ol = new GlideRecord('alm_license');
		ol.get(olde);
		var newesm = nl.software_model.toString();

		var eu = new GlideRecord('alm_entitlement_user');
		eu.addQuery('licensed_by',olde.toString());
		eu.query();
		while(eu.next()){
			eu.licensed_by = newe;
			eu.license_key = '';
			eu.allocated_model = newesm;
			eu.update();
			users.push(eu.assigned_to.name);
			//msg += '' + eu.getValue('assigned_to') + ' added\n';
		}
		var obj = {
			from:ol.display_name.toString(),
			to:nl.display_name.toString(),
			users:users.toString()
		};
		var msg = '';
		msg += 'All user allocations have been moved from (' + ol.display_name.toString() + ') to (' + nl.display_name.toString() + ')';
		msg += '\n\nUsers moved: ' + users.toString();
		ol.work_notes = msg;
		ol.update();
		return JSON.stringify(obj);
	}
});