Collect ServiceNow Security logs
This document explains how to send ServiceNow security events to Google Security Operations using a webhook feed. The ServiceNow Security parser is designed for ServiceNow Instance Security Center security events, such as failed logins, SNC logins, administrator logins, impersonation, and security elevation (see ServiceNow Instance Security Center security events). A ServiceNow integration posts each event as JSON to a Google SecOps webhook endpoint, where the parser normalizes it to the Unified Data Model (UDM).
The parser recognizes five event values: Failed Login, Admin Login, SNC Login, Impersonation, and Security Elevation. Each value corresponds to a registered ServiceNow event in the sysevent queue. This document creates a webhook feed, a Script Include that POSTs to the feed, and four Script Actions that fire on the matching sysevent records and call the Script Include.
After this one-time setup, ServiceNow forwards each new security event to Google SecOps automatically. No further administrator action is required.
Before you begin
Ensure that you have the following prerequisites:
- A Google SecOps instance.
- A ServiceNow instance with a user account that has the
adminrole, or rights to System Definition > Script Includes, System Properties, and System Policy > Events > Script Actions and Registry. - An existing ServiceNow Instance Security Center activation. ISC is end-of-sale and cannot be activated on new instances.
- Privileged access to the Google Google Cloud console (for API key creation).
Create a webhook feed in Google SecOps
The following sections describe how to create a webhook feed in Google SecOps.
Create the feed
- Go to SIEM Settings > Feeds.
- Click Add New Feed.
- On the next page, click Configure a single feed.
- In the Feed name field, enter a name for the feed (for example,
ServiceNow Security Events). - Select Webhook as the Source type.
- Select ServiceNow Security as the Log type.
- Click Next.
Specify values for the following input parameters:
- Split delimiter: enter
\nwhen the integration sends more than one JSON event per request. Leave empty if each request contains a single event. - Asset namespace: the asset namespace.
- Ingestion labels: the label applied to the events from this feed.
- Split delimiter: enter
Click Next.
Review your new feed configuration in the Finalize screen, and then click Submit.
Generate and save the secret key
- On the feed details page, click Generate Secret Key.
Copy and save the secret key in a secure location.
Get the feed endpoint URL
- Go to the Details tab of the feed.
In the Endpoint Information section, copy the Feed endpoint URL. The URL format is:
https://malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateor, for a regional endpoint:
https://<REGION>-malachiteingestion-pa.googleapis.com/v2/unstructuredlogentries:batchCreateSave this URL for a later step.
Click Done.
Create a Google Cloud API key
- Go to the Google Google Cloud console Credentials page.
- Select the project associated with your Google SecOps instance.
- Click Create credentials > API key.
- Click Edit API key to restrict the key.
- In the Name field, enter a descriptive name (for example,
ServiceNow Webhook API Key). Under API restrictions:
- Select Restrict key.
- In the Select APIs list, select Google SecOps API.
Click Save.
Copy the API key and save it in a secure location.
Store Google SecOps credentials in ServiceNow
Store the endpoint URL and credentials as ServiceNow system properties so that the integration code does not contain secrets.
- In ServiceNow, go to System Properties > sys_properties.list.
Click New and provide the following configuration details:
- Name:
x_chronicle.endpoint_url - Value: Paste the feed endpoint URL.
- Type:
string
- Name:
Click Submit.
Click New and provide the following configuration details:
- Name:
x_chronicle.api_key - Value: Paste the Google Cloud API key.
- Type:
password2
- Name:
Click Submit.
Click New and provide the following configuration details:
- Name:
x_chronicle.secret_key - Value: Paste the Google SecOps feed secret key.
- Type:
password2
- Name:
Click Submit.
Create the webhook utility Script Include
This Script Include posts a JSON payload to the Google SecOps webhook endpoint using the ServiceNow sn_ws.RESTMessageV2 scoped API. The API key and secret key are sent as HTTP headers.
- Go to System Definition > Script Includes.
- Click New.
Provide the following configuration details:
- Name:
ChronicleWebhookUtil - API Name:
ChronicleWebhookUtil - Client callable: Unchecked
- Active: Checked
- Name:
In the Script field, enter the following code:
var ChronicleWebhookUtil = Class.create(); ChronicleWebhookUtil.prototype = { initialize: function() { this.endpointURL = gs.getProperty('x_chronicle.endpoint_url'); this.apiKey = gs.getProperty('x_chronicle.api_key'); this.secretKey = gs.getProperty('x_chronicle.secret_key'); }, // payload: a plain object whose keys match the required JSON contract. sendEvent: function(payload) { try { if (!this.endpointURL || !this.apiKey || !this.secretKey) { gs.error('[Chronicle] Missing configuration. Check System Properties: x_chronicle.*'); return false; } var request = new sn_ws.RESTMessageV2(); request.setEndpoint(this.endpointURL); request.setHttpMethod('POST'); request.setRequestHeader('Content-Type', 'application/json'); request.setRequestHeader('X-goog-api-key', this.apiKey); request.setRequestHeader('X-Webhook-Access-Key', this.secretKey); request.setRequestBody(JSON.stringify(payload)); var response = request.execute(); var statusCode = response.getStatusCode(); if (statusCode == 200 || statusCode == 201 || statusCode == 204) { gs.info('[Chronicle] Event sent: ' + payload.event + ' | Status: ' + statusCode); return true; } gs.error('[Chronicle] Failed to send event: ' + payload.event + ' | Status: ' + statusCode + ' | Response: ' + response.getBody()); return false; } catch (ex) { gs.error('[Chronicle] Exception sending event: ' + ex.message); return false; } }, type: 'ChronicleWebhookUtil' };Click Submit.
Verify the event registry
A Script Action only fires when its event name exists in the Event Registry (sysevent_register table). ServiceNow ships the five events used by this integration out of the box, but it's worth confirming because a missing entry produces no error, and the Script Action never runs.
- In ServiceNow, go to System Policy > Events > Registry (
sysevent_register.list). Filter the Event name column for each of the following entries and confirm that a record exists:
loginlogin.failedimpersonation.startsecurity.elevated_role.enabled
If an entry is missing, click New, set the Event name field to the missing value, set Table to
Global [global], and click Submit.
Create the Script Actions
Each Script Action subscribes to one ServiceNow event name. When ServiceNow inserts a matching record into the sysevent queue, the Script Action runs, reads the event parameters, and calls ChronicleWebhookUtil to POST the payload.
The parser maps to the following ServiceNow events:
Parser event value |
ServiceNow event name | Trigger |
|---|---|---|
Failed Login |
login.failed |
Failed login attempt (any authentication method). |
Admin Login |
login |
Successful login by a user with the admin role. |
SNC Login |
login |
Successful login by a user with the snc_external role. |
Impersonation |
impersonation.start |
An administrator starts impersonating another user. |
Security Elevation |
security.elevated_role.enabled |
A user elevates to security_admin or another high-privilege role. |
login and login.failed set parm1 to the username and parm2 to the source IP. The other events follow the same parm1/parm2 convention but the exact contents depend on your ServiceNow release. Open the registry entry for each event and confirm that the Parameters description matches the script described later. Adjust the parm1 or parm2 references if your registry differs.
Create the Failed Login Script Action
- Go to System Policy > Events > Script Actions.
- Click New.
Provide the following configuration details:
- Name:
Chronicle - Failed Login - Event name:
login.failed - Active: Checked
- Name:
In the Script field, enter the following code:
(function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event) { new ChronicleWebhookUtil().sendEvent({ event: 'Failed Login', event_created: event.sys_created_on.getValue(), user: event.parm1.toString(), ip_address: event.parm2.toString() }); })(current, event);Click Submit.
Create the Login Script Action
This Script Action fires on every successful login and forwards the record only when the logged-in user holds the admin or snc_external role. The event field is set accordingly.
- Go to System Policy > Events > Script Actions.
- Click New.
Provide the following configuration details:
- Name:
Chronicle - Login - Event name:
login - Active: Checked
- Name:
In the Script field, enter the following code:
(function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event) { var userName = event.parm1.toString(); var ipAddress = event.parm2.toString(); var user = new GlideRecord('sys_user'); if (!user.get('user_name', userName)) { return; } function hasRole(roleName) { var role = new GlideRecord('sys_user_has_role'); role.addQuery('user', user.sys_id); role.addQuery('role.name', roleName); role.query(); return role.next(); } var eventValue; if (hasRole('snc_external')) { eventValue = 'SNC Login'; } else if (hasRole('admin')) { eventValue = 'Admin Login'; } else { return; } new ChronicleWebhookUtil().sendEvent({ event: eventValue, event_created: event.sys_created_on.getValue(), user: userName, ip_address: ipAddress }); })(current, event);Click Submit.
Create the Impersonation Script Action
- Go to System Policy > Events > Script Actions.
- Click New.
Provide the following configuration details:
- Name:
Chronicle - Impersonation - Event name:
impersonation.start - Active: Checked
- Name:
In the Script field, enter the following code:
(function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event) { new ChronicleWebhookUtil().sendEvent({ event: 'Impersonation', event_created: event.sys_created_on.getValue(), user: event.parm1.toString(), // impersonator snc_user: event.parm2.toString() // impersonated user }); })(current, event);Click Submit.
Create the Security Elevation Script Action
- Go to System Policy > Events > Script Actions.
- Click New.
Provide the following configuration details:
- Name:
Chronicle - Security Elevation - Event name:
security.elevated_role.enabled - Active: Checked
- Name:
In the Script field, enter the following code:
(function runAction(/*GlideRecord*/ current, /*GlideRecord*/ event) { new ChronicleWebhookUtil().sendEvent({ event: 'Security Elevation', event_created: event.sys_created_on.getValue(), user: event.parm1.toString() }); })(current, event);Click Submit.
Verify the integration
Use either of the following approaches.
Verify with a synthetic event
To test each Script Action immediately without waiting for a real security event, do the following to queue the event from a background script:
- In ServiceNow, go to System Definition > Scripts - Background.
In the Run script field, enter one of the following lines and click Run script:
gs.eventQueue('login.failed', null, 'test.user', '203.0.113.45'); gs.eventQueue('login', null, 'admin', '203.0.113.45'); gs.eventQueue('impersonation.start', null, 'admin', 'test.user'); gs.eventQueue('security.elevated_role.enabled', null, 'admin', 'security_admin');Wait up to one minute for the Event Manager job to process the queue.
Verify with a real event
Produce one of the following mapped security events in ServiceNow, to verify the integration:
- Failed Login: open
/login.doin a private browser window and submit an invalid password. - Admin Login: sign in with a user that has the
adminrole from an IP address that is different from your usual one. - Impersonation: from an administrator account, impersonate another user.
- Security Elevation: elevate to the
security_adminrole from a user that has theadminrole.
Confirm forwarding
- In ServiceNow, go to System Policy > Events > Event Log (
sysevent.list) and confirm that a record with the corresponding event name appears. - In ServiceNow, go to System Logs > System Log > All and confirm that a
[Chronicle] Event sent: ...info message appears with status 200, 201, or 204. An[Chronicle] Failed to send eventor[Chronicle] Exception sending eventmessage indicates a credential or connectivity problem. - In Google SecOps, run a UDM search for
metadata.vendor_name = "SERVICENOW"andmetadata.product_name = "SERVICENOW_SECURITY". - Confirm that the event appears with
metadata.event_typeset toUSER_LOGINorUSER_CHANGE_PERMISSIONSand thatprincipal.user.useridis populated.
Authentication methods reference
Google SecOps webhook feeds accept the API key and secret key either as HTTP headers or as URL query parameters. This guide uses the headers method because sn_ws.RESTMessageV2 supports custom headers, which keeps credentials out of URLs and server access logs.
Headers used by this guide:
X-goog-api-key: the Google Cloud API key for the Google SecOps API.X-Webhook-Access-Key: the feed secret key generated by Google SecOps.
If an integration can't send custom headers, append the credentials to the endpoint URL instead:
<ENDPOINT_URL>?key=<API_KEY>&secret=<SECRET_KEY>
Webhook limits and best practices
| Limit | Value |
|---|---|
| Max request size | 4 MB |
| Max QPS (queries per second) | 15,000 |
| Request timeout | 30 seconds |
| Retry behavior | Automatic with exponential backoff |
To control volume, send only the security events your detections require, and batch multiple events per request with the \n split delimiter when the source produces events in bursts.
For more information about Google SecOps feeds, see the Google SecOps feeds documentation. For information about requirements for each feed type, see Feed configuration by type.
If you encounter issues when you create feeds, contact Google SecOps support.
UDM mapping table
| Log field | UDM mapping | Logic |
|---|---|---|
event |
extensions.auth.type |
Mapped: "Failed Login", "SNC Login", "Admin Login", "Impersonation" → MACHINE |
event_created |
metadata.event_timestamp |
Parsed as yyyy-MM-dd HH:mm:ss |
event_type |
metadata.event_type |
Renamed/mapped |
event |
metadata.product_event_type |
Mapped when event != `` |
ip_address |
principal.ip |
Merged |
user |
principal.user.userid |
Mapped when user != `` |
created_by |
target.user.userid |
Mapped when created_by != `` |
snc_user |
target.user.userid |
Mapped when snc_user != `` |
Need more help? Get answers from Community members and Google SecOps professionals.