There has been a surge recently in integrating SNOW (ServiceNow) with IDN (IdentityNow) to generate tickets during provisioning or certification events. Many use-cases come to mind: For example, on an identity joiner event you might want to create SD ticket to give laptop and phones to an employee. Or give / remove an application access which is not directly integrated with IDN. Below I will show you how to get up and running in less than 30 min with the integration
Notes
- This integration is fast changing and the guide is correct as of 30th April 2021
- This integration is an additional license cost to IDN. Please talk to your CSM about it.
- This assume you already have a SNOW tenant in your company
- The guide doesn’t go in-depth into advance scenarios requiring Velocity or rules.
Prerequisite
- SailPoint for Service Desk Installed on SNOW tenant
- SNOW Governance connector installed on IDN and users correlated between IDN and SNOW connector
- Configure at least one virtual appliance cluster.
- Configure at least one source to generate manual tasks for Provisioning.
- Be familiar with Apache Velocity scripting.
- Know the authentication option (Basic or OAuth2) to use for IdentityNow to authenticate into ServiceNow. For token generation and OAuth Client setup on the ServiceNow instance, see Create an endpoint for clients to access the instance.
- Have the permissions requirements for a service account: A Service Desk Administrator must be assigned the x_sap_sdim.admin role.
Gather Information
- Create a source which is going to be used for provisioning and has entitlements a user can request. In this example a flat file source with 3 entitlements.
- Create an Access Profile and Requestable Role for this source
- To set an integration source owner, select an org admin user and get their identity ID via API call {{api-url}}/v3/search 2c91808673a16fb20173b8523ecd0021
- Get the Cluster ID of your VA via API Call {{api-url}}/beta/managed-clusters 2c918088737cf45e0173b8fb6727040d
- Get the id for the provision source (Say flat file) created via {{api-url}}/beta/sources/ : 2c91808578daf04e0178dcb83b1a0173
- Get the id for the SNOW governance source which correlates users between SNOW and IDN via {{api-url}}/beta/sources/ : 2c91808478daf0580178dcff779001b4
- Get the SNOW Application sys_id from SNOW. 8053xxxedbffb300exxxxxxxdbxxxxcx123
Integration
Use the following API to create the integration: https://developer.sailpoint.com/apis/beta/#operation/createServiceDeskIntegration
Please populate the payload (attached separately) and replace the parameters with value
Parameter |
Value |
<ownerId> |
2c91808673a16fb20173b8523ecd0021 |
<clusterId> |
2c918088737cf45e0173b8fb6727040d |
<Provision_Source_ID> |
2c91808578daf04e0178dcb83b1a0173 |
<SNOW_Sys_ID> |
8053xxxedbffb300exxxxxxxdbxxxxcx123 |
<SNOW_URL> |
https://XXXX.service-now.com/ |
<requesterSource> |
2c91808478daf0580178dcff779001b4 |
<username> |
SNOW user |
<password> |
User Password |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
{ "name": "ServiceNow New SDIM", "description": "ServiceNow Service Desk Integration for IdentityNow", "type": "ServiceNow SDIM", "ownerRef": { "type": "IDENTITY", "id": "<ownerId>" }, "clusterRef": { "type": "CLUSTER", "id": "<clusterId>" }, "cluster": null, "managedSources": [], "provisioningConfig": { "universalManager": false, "managedResourceRefs": [{ "type": "SOURCE", "id": "<Provision_Source_ID>" } ], "planInitializerScript": { "source": "\n \n import sailpoint.object.Identity;\n import sailpoint.object.Link;\n import sailpoint.object.IntegrationConfig;\n import sailpoint.object.Application;\n import sailpoint.api.IdentityService;\n\n /**\n * Fields\n */\n String applicationType = \"ServiceNow\";\n // Identity attribute on SailPoint/ UserID on ServiceNow\n String userId = \"sys_id\";\n\n /**\n * Get ServiceNow Account name for identity using application type - ServiceNow\n */\n private String getLinkByAppType(Identity identity) {\n if (identity == null) {\n return null;\n }\n\n List links = identity.getLinks();\n String linkName = null;\n if (links != null ) {\n for (Link link : links) {\n String appType = link.getApplication().getType();\n if (appType != null && appType.equals(applicationType)) {\n if(link.getAttribute(userId) != null) {\n String userName = link.getAttribute(userId);\n if(userName != null) {\n linkName = userName;\n break;\n }\n }\n }\n }\n }\n\n return linkName;\n }\n\n /**\n * Get ServiceNow Account name for Identity using application name\n */\n private String getLinkByAppName(Identity identity, String requesterSource) {\n if (identity == null) {\n return null;\n }\n\n String linkName = null;\n if (requesterSource != null) {\n Application app = context.getObjectByName(Application.class, requesterSource);\n IdentityService identService = new IdentityService(context);\n List links = identService.getLinks(identity, app);\n Link link = null;\n if (links != null && links.size() > 0) {\n link = links.get(0);\n }\n\n if (link != null) {\n String userName = (String) link.getAttribute(userId);\n if (userName != null) {\n linkName = userName;\n }\n }\n }\n\n return linkName;\n }\n\n ////////////////////////////////////////////////////////////////\n // Main\n ////////////////////////////////////////////////////////////////\n Map arguments = (Map)plan.getArguments();\n List requesterList = plan.getRequesters();\n Identity requester = null;\n if (requesterList != null) {\n requester = requesterList.get(0);\n if (requester != null && requester.getName() != null) {\n requester = context.getObjectByName(Identity.class, requester.getName());\n }\n }\n\n String appName = integration.getName();\n if (appName != null) {\n Application appObject = context.getObjectByName(Application.class, appName);\n requesterSource = appObject.getAttributeValue(\"requesterSource\");\n }\n\n // Get ServiceNow Account name\n if (requesterSource != null) {\n openedBy = getLinkByAppName(requester, requesterSource);\n requestedFor = getLinkByAppName(identity, requesterSource);\n } else {\n openedBy = getLinkByAppType(requester);\n requestedFor = getLinkByAppType(identity);\n }\n\n if (requestedFor != null) {\n arguments.put(\"requested_for\", requestedFor);\n }\n\n if (openedBy != null) {\n arguments.put(\"opened_by\", openedBy);\n }\n \n " } }, "attributes": { "serviceRequest": { "checkStatus": { "statusMap": { "closed_complete": "Committed", "closed_rejected": "Failed", "requested": "Queued", "in_process": "Queued", "closed_cancelled": "Failed", "closed_incomplete": "Failed", "closed_skipped": "Committed" }, "resource": "/api/now/table/sc_request?number=$ticketId&sysparm_fields=request_state", "responseElement": "$.result[0].request_state", "checkStatusQueryParam": null }, "provision": { "request": { "short_description": "SailPoint Access Request $!plan.arguments.identityRequestId", "opened_by": "$!plan.arguments.opened_by", "req_description": "Service Request created by Service Desk Integration Module (Service Desk).", "description": "#if($request.operation == 'Create') Create Account on application $request.resource #else For $request.id in application $request.resource #end #if ($request.items) $newline #foreach ($item in $request.items) #if ($item.name == '*disabled*' && $item.value == 'true') Disable Account. $newline #elseif ($item.name == '*disabled*' && $item.value == 'false') Enable Account. $newline #elseif ($item.name == '*locked*' && $item.value == 'false') Unlock Account. $newline #else $!item.Operation $item.name: $item.value $newline #end #end #else $newline $!request.Operation Account #end", "requested_for": "$!plan.arguments.requested_for" }, "requestRootElement": "items", "resource": "/api/x_sap_sdim/sailpoint_cart_js_api/create_ticket", "responseElement": "$.result.request_number", "catalogItem": { "<Provision_Source_ID>": "<SNOW_Sys_ID>" } } }, "ticketType": "serviceRequest", "authenticationType": "Basic", "password": "<password>", "requesterSource": "<requesterSource>", "url": "<SNOW_URL>", "username": "<username>" } } |
Integration Testing
- For the provision source, create some Entitlements and create Access Profile and requestable role.
- Request for a SNOW correlated user for the source
- In Account Activity a pending request will show for the request with REQ Ticket Number
- In SNOW “Service Catalog -> Requests” the ticket number shows up
- Click on RITM and you will see the description of request
- Once the ticket is closed, the Access Request in IDN will close after interval checks. This frequency can be configured as well (See Configuring a Schedule for Status Checks section in SDIM Integration API Reference Guide)
That should be it. You should be able to create provisioning tickets for the source now.
Hope that helped!!!