Configuring ServiceNow Service Catalog approval definitions and rules

Description

Come join Expert Ambassador Mark Cheek as he explains how ServiceNow’s extensibility can be used to make access approvals in the Service Catalog Integration as dynamic as your business needs require. This hands-on demonstration will provide an overview of how to configure approval definitions and rules and use ServiceNow scripts to create complex approval rules.

Additional Resources

N/A

4 Likes

Thank you so much for going through this.

I was wondering if I could pick your brain - I have basically copied all of the code as a proof of concept - I am having an issue getting the group member emails to populate correctly.

I have created a new Role , gave it a tag , create a new governance group

example
Role: Test-Remote Access
Tag: WORKGROUP_RA_APPROVAL
Governance Group: RA_APPROVAL ( i also tried it with a space)

I can get ServiceNow to get all the way through the script until it gets to the parsing of the email’s

I was able to log and I get this error twice

{“detailCode”:“404 Not found”,“trackingId”:“TrackingIDhere”,“messages”:[{“locale”:“und”,“localeOrigin”:“REQUEST”,“text”:“The server did not find a current representation for the target resource.”},{“locale”:“en-US”,“localeOrigin”:“DEFAULT”,“text”:“The server did not find a current representation for the target resource.”}],“causes”:}

But right after it actually parses/logs the data
[{“email":"[email protected]”,“type”:“IDENTITY”,“id”:“randomID”,“name”:“user1, Ashley”},{“email":"[email protected]”,“type”:“IDENTITY”,“id”:“randomID”,“name”:“user2, Ben”}]

It seems the API is giving a 404 twice and then says oh wait I do see the data here it is - but by that time it is already defaulting to the internal security group.

Is there something else I should be doing or could look at - I know your setup was using Access Profiles in the video.

Can you post a sample of that section of your script?

1 Like

@mcheek by chance have you had a moment to check the above code?

Sorry for the delay.

It seems like the 404 would be a response coming through the ISC API… it seems if you have the two records of the users who are members of the governance groups (with their email addresses), you’d then be looking them up in ServiceNow (using a GlideRecord query against sys_user) using the email addresses you got from that response.

Am I following this correctly?

correct, it just seems like it gives a 404 for the first few attempts then magically gets them but it already has stopped processing by that point.

For some reason, it never occurred to me that I hadn’t posted my actual script I wrote for this livestream. It’s below, feel free to use in your approval rule

(function executeRule(currentRecord) {

	var approvers = [];
	var approver_emails = [];

	//Put the ServiceNow group you want to use as a 'fall back' approval group in case an approver can't be found
	var defaultApproverGroup = 'SEC - Identity Management';

	var url = gs.getProperty('x_sap_intidn.x_sp_spnt_snow_int.spnt_endpoint_url', null);
	var access_token = getAccessToken(url);

	var endpoint;
	if (currentRecord.variables.u_access_type == 'ACCESS_PROFILE') {
		endpoint = getAPIEndpoint('searchAccessProfiles');
	}
	if (currentRecord.variables.u_access_type == 'ROLE') {
		endpoint = getAPIEndpoint('searchRoles');
	}
	if (currentRecord.variables.u_access_type == 'ENTITLEMENT') {
		endpoint = getAPIEndpoint('searchEntitlements');
	}

	var request = new sn_ws.RESTMessageV2();
	var finalURL = url + endpoint + '/' + currentRecord.variables.u_access_id.toString();

	request.setHttpMethod('GET');
	request.setEndpoint(finalURL);
	request.setRequestHeader("Accept", "application/json");
	request.setRequestHeader('Authorization', 'Bearer ' + access_token);

	var response = request.execute();
	var responseBody = response.getBody();
	var httpStatus = response.getStatusCode();

	if (httpStatus == '200') {
		var payload = JSON.parse(responseBody);
		//a group owned by chk_admin means it's approved by a Governance Group
		if (payload.owner.name != 'chk_admin') {
			if (payload.owner.hasOwnProperty('email')) {
				approver_emails.push(payload.owner.email.toLowerCase());
			} else {
				assignToSecurity();
			}

		}
		if (payload.owner.name == 'chk_admin' && payload.hasOwnProperty('tags') && JSON.stringify(payload.tags).indexOf('WORKGROUP_') != -1) {
			//get the group name from the tags
			var ownergroup;
			for (var i = 0; i < payload.tags.length; i++) {
				if (payload.tags[i].indexOf('WORKGROUP_') > -1) {
					ownergroup = payload.tags[i].substring(10).replace('_', ' ');
				}
			}
			//get a list of all work groups and get the ID by looking up the name

			var get_groups = new sn_ws.RESTMessageV2();
			get_groups.setHttpMethod('GET');
			get_groups.setEndpoint(url + getAPIEndpoint('getAllGovernanceGroups'));
			get_groups.setRequestHeader("Accept", "application/json");
			get_groups.setRequestHeader('Authorization', 'Bearer ' + access_token);

			var groups = get_groups.execute();
			var groups_body = groups.getBody();
			var groups_status = groups.getStatusCode();

			if (groups_status == '200') {
				var group_id;
				var groups_payload = JSON.parse(groups_body);

				for (i = 0; i < groups_payload.length; i++) {
					if (groups_payload[i].name.toUpperCase() == ownergroup) {
						group_id = groups_payload[i].id;
					}
				}
				//Get the members of the work group and push them to the approver variable
				var get_group_members = new sn_ws.RESTMessageV2();
				get_group_members.setHttpMethod('GET');
				get_group_members.setEndpoint(url + getAPIEndpoint('getGovernanceGroupMembersById').replace('%groupId%', group_id));
				get_group_members.setRequestHeader("Accept", "application/json");
				get_group_members.setRequestHeader('Authorization', 'Bearer ' + access_token);

				var group_members = get_group_members.execute();
				var group_members_body = group_members.getBody();
				var group_members_status = group_members.getStatusCode();
				var group_members_payload = JSON.parse(group_members_body);

				if (group_members_status == '200') {
					if (group_members_payload.length > 0) {
						for (i = 0; i < group_members_payload.length; i++) {
							approver_emails.push(group_members_payload[i].email);
						}
					} else {
						approver_emails = assignToDefaultGroup(defaultApproverGroup);
					}
				}
			}

		} else if (payload.owner.name == 'chk_admin' && (!payload.hasOwnProperty('tags') || !JSON.stringify(payload.tags).indexOf('WORKGROUP_') != -1)) {
			approver_emails = assignToDefaultGroup(defaultApproverGroup);
		}
		approver_emails.forEach(function (email) {
			approvers.push(getuserbyemail(email));
		});

	}

	return approvers;

	function getAccessToken(url) {
		var client_id = gs.getProperty('x_sap_intidn.x_sp_spnt_snow_int.spnt_client_id', null);
		var client_secret = gs.getProperty('x_sap_intidn.x_sp_spnt_snow_int.spnt_client_secret', null);


		var endpoint_token = '';
		var endpointConfig_token = new GlideRecord('x_sap_intidn_sailpoint_identitynow_links');
		endpointConfig_token.addQuery('action', 'auth');

		endpointConfig_token.query();

		if (endpointConfig_token.next()) {
			endpoint_token = endpointConfig_token.getValue('link');
		}
		var finalURL_token = url + endpoint_token;

		var request_token = new sn_ws.RESTMessageV2();
		request_token.setEndpoint(finalURL_token);
		request_token.setHttpMethod('POST');
		request_token.setBasicAuth(client_id, client_secret);
		request_token.setQueryParameter("grant_type", "client_credentials");

		var response_token = request_token.execute();
		var responseBody_token = response_token.getBody();
		var responseBody_parse = JSON.parse(responseBody_token);

		return responseBody_parse.access_token;
	}

	function getAPIEndpoint(action) {
		var gr_endpoint = new GlideRecord('x_sap_intidn_sailpoint_identitynow_links');
		gr_endpoint.get('action', action);
		return gr_endpoint.link;
	}

	function assignToDefaultGroup(group) {
		var members = [];
		var gr_member = new GlideRecord('sys_user_grmember');
		gr_member.addEncodedQuery('group.name=' + group);
		gr_member.query();

		while (gr_member.next()) {
			members.push(gr_member.user.email.toString());
		}

		return members;
	}

	function getuserbyemail(email) {
		var gr = new GlideRecord('sys_user');
		gr.get('email', email);
		if (gr.sys_id.toString() == currentRecord.variables.requested_for.sys_id.toString()) {
			return gr.manager.sys_id;
		}
		return gr.sys_id;
	}

})(currentRecord);
3 Likes

Thanks! yours appears to be working! I must have some type of typo or error in the script. I appreciate you posting this!!

1 Like