ServiceNow IAM
Integrate with ServiceNow's services to execute CRUD operations for employee lifecycle processes.
Identity and Access Management · ServiceNow
Details
| ID | ServiceNow IAM |
|---|---|
| Provider | ServiceNow |
| Category | Identity and Access Management |
| From Version | 6.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Attack Surface Management Exposure Management Agentix Cortex Cloud Cloud Runtime Security Cloud Posture Security XSIAM EDR |
README
Note: This integration should be used along with our IAM premium pack. For further details, visit our IAM pack documentation.
Integrate with ServiceNow’s services to perform Identity Lifecycle Management operations.
This integration was integrated and tested with London version of ServiceNow.
For more information, please refer to the Identity Lifecycle Management article.
Configure ServiceNow IAM in Cortex
| Parameter | Description | Required |
|---|---|---|
| ServiceNow URL (https://domain.service-now.com) | True | |
| ServiceNow API Version (e.g. ‘v1’). Specify this value to use an endpoint version other than the latest. | False | |
| Username | True | |
| Password | True | |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False | |
| Allow creating users | False | |
| Allow updating users | False | |
| Allow enabling users | False | |
| Allow disabling users | False | |
| Automatically create user if not found in update command | False | |
| Incoming Mapper | True | |
| Outgoing Mapper | Cortex XSOAR only parameter. | False |
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
iam-create-user
Creates a user.
Base Command
iam-create-user
Input
| Argument Name | Description | Required |
|---|---|---|
| user-profile | User Profile indicator details. | Required |
| allow-enable | Enable the user. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| IAM.Vendor.active | Boolean | If true, the employee’s status is active, otherwise false. |
| IAM.Vendor.brand | String | Name of the integration. |
| IAM.Vendor.details | string | Indicates if the API was successful or provides error information. |
| IAM.Vendor.email | String | The employee’s email address. |
| IAM.Vendor.errorCode | Number | HTTP error response code. |
| IAM.Vendor.errorMessage | String | Reason why the API failed. |
| IAM.Vendor.id | String | The employee’s user ID in the app. |
| IAM.Vendor.instanceName | string | Name of the integration instance. |
| IAM.Vendor.success | Boolean | If true, the command was executed successfully, otherwise false. |
| IAM.Vendor.username | String | The employee’s username in the app. |
Command Example
!iam-create-user user-profile=`{"email":"testdemisto2@paloaltonetworks.com", "givenname":"Test","surname":"Demisto"}`
Human Readable Output
Create User Results (ServiceNow IAM)
| brand | instanceName | success | active | id | details | |
|---|---|---|---|---|---|---|
| ServiceNow IAM | ServiceNow IAM_instance_1 | true | true | edab746f1b142410042611b4bd4bcb23 | testdemisto2@paloaltonetworks.com | calendar_integration: 1 country: user_password: last_login_time: source: sys_updated_on: 2020-11-11 14:55:48 building: web_service_access_only: false notification: 2 enable_multifactor_authn: false sys_updated_by: admin sys_created_on: 2020-11-11 14:55:48 sys_domain: {“link”: “https://ven03941.service-now.com/api/now/table/sys_user_group/global”, “value”: “global”} state: vip: false sys_created_by: admin zip: home_phone: time_format: last_login: default_perspective: active: true sys_domain_path: / cost_center: phone: name: Test Demisto employee_number: password_needs_reset: false gender: city: failed_attempts: user_name: roles: title: sys_class_name: sys_user sys_id: edab746f1b142410042611b4bd4bcb23 internal_integration_user: false ldap_server: mobile_phone: street: company: department: first_name: Test email: testdemisto2@paloaltonetworks.com introduction: preferred_language: manager: locked_out: false sys_mod_count: 0 last_name: Demisto photo: avatar: middle_name: sys_tags: time_zone: schedule: date_format: location: |
iam-update-user
Updates an existing user with the data passed in the user-profile argument.
Base Command
iam-update-user
Input
| Argument Name | Description | Required |
|---|---|---|
| user-profile | A User Profile indicator. | Required |
| allow-enable | Enable the user. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| IAM.Vendor.active | Boolean | If true, the employee’s status is active, otherwise false. |
| IAM.Vendor.brand | String | Name of the integration. |
| IAM.Vendor.details | string | Indicates if the API was successful or provides error information. |
| IAM.Vendor.email | String | The employee’s email address. |
| IAM.Vendor.errorCode | Number | HTTP error response code. |
| IAM.Vendor.errorMessage | String | Reason why the API failed. |
| IAM.Vendor.id | String | The employee’s user ID in the app. |
| IAM.Vendor.instanceName | string | Name of the integration instance. |
| IAM.Vendor.success | Boolean | If true, the command was executed successfully, otherwise false. |
| IAM.Vendor.username | String | The employee’s username in the app. |
Command Example
!iam-update-user user-profile=`{"email":"testdemisto2@paloaltonetworks.com", "givenname":"Test","surname":"Demisto_updated"}`
Human Readable Output
Update User Results (ServiceNow IAM)
| brand | instanceName | success | active | id | details | |
|---|---|---|---|---|---|---|
| ServiceNow IAM | ServiceNow IAM_instance_1 | true | true | edab746f1b142410042611b4bd4bcb23 | testdemisto2@paloaltonetworks.com | calendar_integration: 1 country: user_password: last_login_time: source: sys_updated_on: 2020-11-11 14:55:48 building: web_service_access_only: false notification: 2 enable_multifactor_authn: false sys_updated_by: admin sys_created_on: 2020-11-11 14:55:48 sys_domain: {“link”: “https://ven03941.service-now.com/api/now/table/sys_user_group/global”, “value”: “global”} state: vip: false sys_created_by: admin zip: home_phone: time_format: last_login: default_perspective: active: true sys_domain_path: / cost_center: phone: name: Test Demisto_updated employee_number: password_needs_reset: false gender: city: failed_attempts: user_name: roles: title: sys_class_name: sys_user sys_id: edab746f1b142410042611b4bd4bcb23 internal_integration_user: false ldap_server: mobile_phone: street: company: department: first_name: Test email: testdemisto2@paloaltonetworks.com introduction: preferred_language: manager: locked_out: false sys_mod_count: 0 last_name: Demisto_updated photo: avatar: middle_name: sys_tags: time_zone: schedule: date_format: location: |
iam-get-user
Retrieves a single user resource.
Base Command
iam-get-user
Input
| Argument Name | Description | Required |
|---|---|---|
| user-profile | A User Profile indicator. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IAM.Vendor.active | Boolean | If true, the employee’s status is active, otherwise false. |
| IAM.Vendor.brand | String | Name of the integration. |
| IAM.Vendor.details | string | Indicates if the API was successful or provides error information. |
| IAM.Vendor.email | String | The employee’s email address. |
| IAM.Vendor.errorCode | Number | HTTP error response code. |
| IAM.Vendor.errorMessage | String | Reason why the API failed. |
| IAM.Vendor.id | String | The employee’s user ID in the app. |
| IAM.Vendor.instanceName | string | Name of the integration instance. |
| IAM.Vendor.success | Boolean | If true, the command was executed successfully, otherwise false. |
| IAM.Vendor.username | String | The employee’s username in the app. |
Command Example
!iam-get-user user-profile=`{"email":"testdemisto2@paloaltonetworks.com"}`
Human Readable Output
Get User Results (ServiceNow IAM)
| brand | instanceName | success | active | id | details | |
|---|---|---|---|---|---|---|
| ServiceNow IAM | ServiceNow IAM_instance_1 | true | true | edab746f1b142410042611b4bd4bcb23 | testdemisto2@paloaltonetworks.com | calendar_integration: 1 country: user_password: last_login_time: source: sys_updated_on: 2020-11-11 14:55:48 building: web_service_access_only: false notification: 2 enable_multifactor_authn: false sys_updated_by: admin sys_created_on: 2020-11-11 14:55:48 sys_domain: {“link”: “https://ven03941.service-now.com/api/now/table/sys_user_group/global”, “value”: “global”} state: vip: false sys_created_by: admin zip: home_phone: time_format: last_login: default_perspective: active: true sys_domain_path: / cost_center: phone: name: Test Demisto_updated employee_number: password_needs_reset: false gender: city: failed_attempts: user_name: roles: title: sys_class_name: sys_user sys_id: edab746f1b142410042611b4bd4bcb23 internal_integration_user: false ldap_server: mobile_phone: street: company: department: first_name: Test email: testdemisto2@paloaltonetworks.com introduction: preferred_language: manager: locked_out: false sys_mod_count: 0 last_name: Demisto_updated photo: avatar: middle_name: sys_tags: time_zone: schedule: date_format: location: |
iam-disable-user
Disable an active user.
Base Command
iam-disable-user
Input
| Argument Name | Description | Required |
|---|---|---|
| user-profile | A User Profile indicator. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IAM.Vendor.active | Boolean | If true, the employee’s status is active, otherwise false. |
| IAM.Vendor.brand | String | Name of the integration. |
| IAM.Vendor.details | string | Indicates if the API was successful or provides error information. |
| IAM.Vendor.email | String | The employee’s email address. |
| IAM.Vendor.errorCode | Number | HTTP error response code. |
| IAM.Vendor.errorMessage | String | Reason why the API failed. |
| IAM.Vendor.id | String | The employee’s user ID in the app. |
| IAM.Vendor.instanceName | string | Name of the integration instance. |
| IAM.Vendor.success | Boolean | If true, the command was executed successfully, otherwise false. |
| IAM.Vendor.username | String | The employee’s username in the app. |
Command Example
!iam-disable-user user-profile=`{"email":"testdemisto2@paloaltonetworks.com"}`
Human Readable Output
Disable User Results (ServiceNow IAM)
| brand | instanceName | success | active | id | details | |
|---|---|---|---|---|---|---|
| ServiceNow IAM | ServiceNow IAM_instance_1 | true | false | edab746f1b142410042611b4bd4bcb23 | testdemisto2@paloaltonetworks.com | calendar_integration: 1 country: user_password: last_login_time: source: sys_updated_on: 2020-11-11 14:55:48 building: web_service_access_only: false notification: 2 enable_multifactor_authn: false sys_updated_by: admin sys_created_on: 2020-11-11 14:55:48 sys_domain: {“link”: “https://ven03941.service-now.com/api/now/table/sys_user_group/global”, “value”: “global”} state: vip: false sys_created_by: admin zip: home_phone: time_format: last_login: default_perspective: active: false sys_domain_path: / cost_center: phone: name: Test Demisto_updated employee_number: password_needs_reset: false gender: city: failed_attempts: user_name: roles: title: sys_class_name: sys_user sys_id: edab746f1b142410042611b4bd4bcb23 internal_integration_user: false ldap_server: mobile_phone: street: company: department: first_name: Test email: testdemisto2@paloaltonetworks.com introduction: preferred_language: manager: locked_out: false sys_mod_count: 0 last_name: Demisto_updated photo: avatar: middle_name: sys_tags: time_zone: schedule: date_format: location: |
Configuration parameters
url— ServiceNow URL (https://<domain>.service-now.com) (required)api_version— ServiceNow API Version (e.g. 'v1'). Specify this value to use an endpoint version other than the latest.credentials— Username (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settingscreate_user_enabled— Allow creating usersupdate_user_enabled— Allow updating usersenable_user_enabled— Allow enabling usersdisable_user_enabled— Allow disabling userscreate_if_not_exists— Automatically create user if not found in update commandmapper_in— Incoming Mapper (required)mapper_out— Outgoing Mapper
Commands (5)
-
get-mapping-fieldsRetrieves a User Profile schema, which holds all of the user fields within the application. Used for outgoing-mapping through the Get Schema option.
-
iam-create-userCreates a user in ServiceNow.
-
iam-disable-userDisable an active user.
-
iam-get-userRetrieves a single user resource.
-
iam-update-userUpdates an existing user with the data passed in the user-profile argument.
import pytest from IAMApiModule import * from requests import Response, Session from ServiceNow_IAM import ( Client, create_user_command, disable_user_command, get_mapping_fields_command, get_user_command, update_user_command, ) SERVICENOW_USER_OUTPUT = { "sys_id": "mock_id", "user_name": "mock_user_name", "first_name": "mock_first_name", "last_name": "mock_last_name", "active": "true", "email": "testdemisto2@paloaltonetworks.com", } SERVICENOW_DISABLED_USER_OUTPUT = { "sys_id": "mock_id", "user_name": "mock_user_name", "first_name": "mock_first_name", "last_name": "mock_last_name", "active": "false", "email": "testdemisto2@paloaltonetworks.com", } BASE_URL = "https://test.com" def mock_client(): client = Client(base_url=BASE_URL) return client def get_outputs_from_user_profile(user_profile): entry_context = user_profile.to_entry() outputs = entry_context.get("Contents") return outputs @pytest.mark.parametrize( "args, mock_url", [ ( {"user-profile": {"email": "testdemisto2@paloaltonetworks.com"}}, f"{BASE_URL}/table/sys_user?email=testdemisto2@paloaltonetworks.com", ), ( {"user-profile": {"email": "testdemisto2@paloaltonetworks.com", "user_name": "mock_user_name"}}, f"{BASE_URL}/table/sys_user?user_name=mock_user_name", ), ( {"user-profile": {"email": "testdemisto2@paloaltonetworks.com", "id": "mock_id", "user_name": "mock_user_name"}}, f"{BASE_URL}/table/sys_user?sys_id=mock_id", ), ], ) def test_get_user_command__existing_user(mocker, args, mock_url, requests_mock): """ Given: - A ServiceNow IAM client object - A user-profile argument that contains an email of a user When: - The user exists in ServiceNow - Calling function get_user_command Cases: Case a: User profile contains username data. Case b: User profile contains username and email data. Case c: User profile contains username, email and ID data. Then: - Ensure the resulted User Profile object holds the correct user details Cases: Case a: Mocked URL querying by username is called. Case b: Mocked URL querying by email is called. Case c: Mocked URL querying by ID is called. """ client = mock_client() requests_mock.get(mock_url, json={"result": [SERVICENOW_USER_OUTPUT]}) mocker.patch.object(IAMUserProfile, "update_with_app_data", return_value={}) user_profile = get_user_command(client, args, "mapper_in", "mapper_out") outputs = get_outputs_from_user_profile(user_profile) assert outputs.get("action") == IAMActions.GET_USER assert outputs.get("success") is True assert outputs.get("active") is True assert outputs.get("id") == "mock_id" assert outputs.get("username") == "mock_user_name" assert outputs.get("details", {}).get("first_name") == "mock_first_name" assert outputs.get("details", {}).get("last_name") == "mock_last_name" def test_get_user_command__non_existing_user(mocker): """ Given: - A ServiceNow IAM client object - A user-profile argument that contains an email a user When: - The user does not exist in ServiceNow - Calling function get_user_command Then: - Ensure the resulted User Profile object holds information about an unsuccessful result. """ client = mock_client() args = {"user-profile": {"email": "testdemisto2@paloaltonetworks.com"}} mocker.patch.object(client, "get_user", return_value=None) user_profile = get_user_command(client, args, "mapper_in", "mapper_out") outputs = get_outputs_from_user_profile(user_profile) assert outputs.get("action") == IAMActions.GET_USER assert outputs.get("success") is False assert outputs.get("errorCode") == IAMErrors.USER_DOES_NOT_EXIST[0] assert outputs.get("errorMessage") == IAMErrors.USER_DOES_NOT_EXIST[1] def test_get_user_command__bad_response(mocker): """ Given: - A ServiceNow IAM client object - A user-profile argument that contains an email of a non-existing user in ServiceNow When: - Calling function get_user_command - A bad response (500) is returned from ServiceNow's API Then: - Ensure the resulted User Profile object holds information about the bad response. """ import demistomock as demisto client = mock_client() args = {"user-profile": {"email": "testdemisto2@paloaltonetworks.com"}} bad_response = Response() bad_response.status_code = 500 bad_response._content = b'{"error": {"detail": "details", "message": "message"}}' mocker.patch.object(demisto, "error") mocker.patch.object(Session, "request", return_value=bad_response) user_profile = get_user_command(client, args, "mapper_in", "mapper_out") outputs = get_outputs_from_user_profile(user_profile) assert outputs.get("action") == IAMActions.GET_USER assert outputs.get("success") is False assert outputs.get("errorCode") == 500 assert outputs.get("errorMessage") == "message: details" def test_create_user_command__success(mocker): """ Given: - A ServiceNow IAM client object - A user-profile argument that contains an email of a non-existing user in ServiceNow When: - Calling function create_user_command Then: - Ensure a User Profile object with the user data is returned """ client = mock_client() args = {"user-profile": {"email": "testdemisto2@paloaltonetworks.com"}} mocker.patch.object(client, "get_user", return_value=None) mocker.patch.object(client, "create_user", return_value=SERVICENOW_USER_OUTPUT) user_profile = create_user_command( client, args, "mapper_out", is_command_enabled=True, is_update_enabled=False, is_enable_enabled=False ) outputs = get_outputs_from_user_profile(user_profile) assert outputs.get("action") == IAMActions.CREATE_USER assert outputs.get("success") is True assert outputs.get("active") is True assert outputs.get("id") == "mock_id" assert outputs.get("username") == "mock_user_name" assert outputs.get("details", {}).get("first_name") == "mock_first_name" assert outputs.get("details", {}).get("last_name") == "mock_last_name" def test_create_user_command__user_already_exists(mocker): """ Given: - A ServiceNow IAM client object - A user-profile argument that contains an email of a user When: - The user already exists in ServiceNow and disabled - allow-enable argument is false - Calling function create_user_command Then: - Ensure the command is considered successful and the user is still disabled """ client = mock_client() args = {"user-profile": {"email": "testdemisto2@paloaltonetworks.com"}, "allow-enable": "false"} mocker.patch.object(client, "get_user", return_value=SERVICENOW_DISABLED_USER_OUTPUT) mocker.patch.object(client, "update_user", return_value=SERVICENOW_DISABLED_USER_OUTPUT) user_profile = create_user_command( client, args, "mapper_out", is_command_enabled=True, is_update_enabled=True, is_enable_enabled=True ) outputs = get_outputs_from_user_profile(user_profile) assert outputs.get("action") == IAMActions.UPDATE_USER assert outputs.get("success") is True assert outputs.get("active") is False assert outputs.get("id") == "mock_id" assert outputs.get("username") == "mock_user_name" assert outputs.get("details", {}).get("first_name") == "mock_first_name" assert outputs.get("details", {}).get("last_name") == "mock_last_name" def test_update_user_command__non_existing_user(mocker): """ Given: - A ServiceNow IAM client object - A user-profile argument that contains user data When: - The user does not exist in ServiceNow - create-if-not-exists parameter is checked - Create User command is enabled - Calling function update_user_command Then: - Ensure the create action is executed - Ensure a User Profile object with the user data is returned """ client = mock_client() args = {"user-profile": {"email": "testdemisto2@paloaltonetworks.com", "givenname": "mock_first_name"}} mocker.patch.object(client, "get_user", return_value=None) mocker.patch.object(client, "create_user", return_value=SERVICENOW_USER_OUTPUT) user_profile = update_user_command( client, args, "mapper_out", is_command_enabled=True, is_enable_enabled=False, is_create_user_enabled=True, create_if_not_exists=True, ) outputs = get_outputs_from_user_profile(user_profile) assert outputs.get("action") == IAMActions.CREATE_USER assert outputs.get("success") is True assert outputs.get("active") is True assert outputs.get("id") == "mock_id" assert outputs.get("username") == "mock_user_name" assert outputs.get("details", {}).get("first_name") == "mock_first_name" assert outputs.get("details", {}).get("last_name") == "mock_last_name" def test_update_user_command__command_is_disabled(mocker): """ Given: - A ServiceNow IAM client object - A user-profile argument that contains user data When: - Update User command is disabled - Calling function update_user_command Then: - Ensure the command is considered successful and skipped """ client = mock_client() args = {"user-profile": {"email": "testdemisto2@paloaltonetworks.com", "givenname": "mock_first_name"}} mocker.patch.object(client, "get_user", return_value=None) mocker.patch.object(IAMUserProfile, "map_object", return_value={}) mocker.patch.object(client, "update_user", return_value=SERVICENOW_USER_OUTPUT) user_profile = update_user_command( client, args, "mapper_out", is_command_enabled=False, is_enable_enabled=False, is_create_user_enabled=False, create_if_not_exists=False, ) outputs = get_outputs_from_user_profile(user_profile) assert outputs.get("action") == IAMActions.UPDATE_USER assert outputs.get("success") is True assert outputs.get("skipped") is True assert outputs.get("reason") == "Command is disabled." def test_update_user_command__allow_enable(mocker): """ Given: - An ServiceNow IAM client object - A user-profile argument that contains user data When: - The user is disabled in ServiceNow - allow-enable argument is true - Calling function update_user_command Then: - Ensure the user is enabled at the end of the command execution. """ client = mock_client() args = { "user-profile": {"email": "testdemisto2@paloaltonetworks.com", "givenname": "mock_first_name"}, "allow-enable": "true", } mocker.patch.object(client, "get_user", return_value=SERVICENOW_DISABLED_USER_OUTPUT) mocker.patch.object(client, "update_user", return_value=SERVICENOW_USER_OUTPUT) user_profile = update_user_command( client, args, "mapper_out", is_command_enabled=True, is_enable_enabled=True, is_create_user_enabled=False, create_if_not_exists=False, ) outputs = get_outputs_from_user_profile(user_profile) assert outputs.get("action") == IAMActions.UPDATE_USER assert outputs.get("success") is True assert outputs.get("active") is True assert outputs.get("id") == "mock_id" assert outputs.get("username") == "mock_user_name" assert outputs.get("details", {}).get("first_name") == "mock_first_name" assert outputs.get("details", {}).get("last_name") == "mock_last_name" def test_disable_user_command__non_existing_user(mocker): """ Given: - A ServiceNow IAM client object - A user-profile argument that contains an email of a user When: - create-if-not-exists parameter is unchecked - The user does not exist in ServiceNow - Calling function disable_user_command Then: - Ensure the command is considered successful and skipped """ client = mock_client() args = {"user-profile": {"email": "testdemisto2@paloaltonetworks.com"}} mocker.patch.object(client, "get_user", return_value=None) user_profile = disable_user_command(client, args, is_command_enabled=True, mapper_out="mapper_out") outputs = get_outputs_from_user_profile(user_profile) assert outputs.get("action") == IAMActions.DISABLE_USER assert outputs.get("success") is True assert outputs.get("skipped") is True assert outputs.get("reason") == IAMErrors.USER_DOES_NOT_EXIST[1] def test_get_mapping_fields_command(mocker): """ Given: - A ServiceNow IAM client object When: - ServiceNow user schema contains the fields 'field1' and 'field2' - Calling function get_mapping_fields_command Then: - Ensure a GetMappingFieldsResponse object that contains the ServiceNow fields is returned """ client = mock_client() mocker.patch.object(client, "get_service_now_fields", return_value={"field1": "desc1", "field2": "desc2"}) mapping_response = get_mapping_fields_command(client) mapping = mapping_response.extract_mapping() assert mapping.get(IAMUserProfile.DEFAULT_INCIDENT_TYPE, {}).get("field1") == "desc1" assert mapping.get(IAMUserProfile.DEFAULT_INCIDENT_TYPE, {}).get("field2") == "desc2"