ServiceNow Deprecated
Deprecated. Use the ServiceNow v2 integration instead.
Case Management · ServiceNow
Details
| ID | ServiceNow |
|---|---|
| Provider | ServiceNow |
| Category | Case Management |
| From Version | 5.0.0 |
| Docker Image | demisto/python:2.7.18.20958 |
| Supported Modules | Attack Surface Management Exposure Management Agentix Cortex Cloud Cloud Runtime Security Cloud Posture Security XSIAM EDR |
README
Deprecated. Use the ServiceNow v2 integration instead.
This integration was integrated and tested with the London version of ServiceNow.
Use cases
- Get, update, create, delete ServiceNow tickets, in addition to adding links/comments and uploading files to them.
- Fetch newly created incidents.
- Get, update, create, delete records from any ServiceNow table.
Wrapper Scripts
There are 3 scripts that serve as examples for wrapping the following generic commands:servicenow-query-table - ServiceNowQueryIncidentservicenow-create-record - ServiceNowCreateIncidentservicenow-update-record - ServiceNowUpdateIncident
You can use these scripts if you want to wrap these commands around a ServiceNow table of your choice. These scripts are wrapped around the incident table, so to wrap them around another table simply copy the scripts and edit the code, arguments and outputs accordingly.
Configure ServiceNow on Cortex XSOAR
To use ServiceNow on Cortex XSOAR, ensure your user account has the rest_api_explorer and web_service_admin roles. These roles are required to make API calls. However, they may not suffice for viewing records in some tables. Please make sure you have the correct role so you have permissions to work with the relevant table.
- Navigate to Settings > Integrations > Servers `&` Services.
- Search for ServiceNow.
- Click Add instance to create and configure a new integration instance.
- Name: a textual name for the integration instance.
- ServiceNow URL, in the format https://company.service-now.com/
- Username
- Use system proxy settings
- Trust any certificate (not secure)
- Default ticket type for running ticket commands and fetching incidents. Can be incident, sc_request, problem or change_reuquest. Commands also take ticket_type argument
- ServiceNow API Version (e.g. 'v1')
- Fetch incidents: The query to use when fetching incidents, and how many incidents to fetch each time.
- The query to use when fetching ServiceNow incidents
- How many ServiceNow incidents to fetch each time
- Incident type
- Get incident attachments
- Click Test to validate the URLs, token, and connection.
Cortex XSOAR engine: If relevant, select the engine that acts as a proxy to the IMAP server.
Engines are used when you need to access a remote network segments and there are network devices such as proxies, firewalls, etc. that prevent the Cortex XSOAR server from accessing the remote networks. For more information, see:
Cortex XSOAR 6.13 - Engines
Cortex XSOAR 8 Cloud- Engines
Cortex XSOAR 8.7 On-prem - Engines.
Fetched Incidents Data
The integration fetches newly created tickets according to the following parameters, which you define in the instance configuration: ticket_type, query, and limit. For the first fetch, the integration will fetch incidents that were created 10 minutes earlier. After that, it will fetch incidents that were created after the timestamp of the last fetch.
Data:
{
"result": [
{
"active": "false",
"activity_due": "",
"additional_assignee_list": "",
"approval": "",
"approval_history": "",
"approval_set": "",
"assigned_to": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user/46b87022a9fe198101a78787e40d7547",
"value": "46b87022a9fe198101a78787e40d7547"
},
"assignment_group": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user_group/d625dccec0a8016700a222a0f7900d06",
"value": "d625dccec0a8016700a222a0f7900d06"
},
"business_duration": "1970-01-22 21:46:21",
"business_service": "",
"business_stc": "1892781",
"calendar_duration": "1970-04-02 20:46:21",
"calendar_stc": "7937181",
"caller_id": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user/5137153cc611227c000bbd1bd8cd2005",
"value": "5137153cc611227c000bbd1bd8cd2005"
},
"category": "network",
"caused_by": "",
"child_incidents": "",
"close_code": "Closed/Resolved by Caller",
"close_notes": "Closed before close notes were made mandatory\n\t\t",
"closed_at": "2018-02-21 23:10:06",
"closed_by": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user/9ee1b13dc6112271007f9d0efdb69cd0",
"value": "9ee1b13dc6112271007f9d0efdb69cd0"
},
"cmdb_ci": {
"link": "https://dev56245.service-now.com/api/now/table/cmdb_ci/b0c4030ac0a800090152e7a4564ca36c",
"value": "b0c4030ac0a800090152e7a4564ca36c"
},
"comments": "",
"comments_and_work_notes": "",
"company": "",
"contact_type": "",
"correlation_display": "",
"correlation_id": "",
"delivery_plan": "",
"delivery_task": "",
"description": "User can't access email on mail.company.com.\n\t\t",
"due_date": "",
"escalation": "0",
"expected_start": "",
"follow_up": "",
"group_list": "",
"hold_reason": "",
"impact": "1",
"incident_state": "7",
"knowledge": "false",
"location": {
"link": "https://dev56245.service-now.com/api/now/table/cmn_location/1083361cc611227501b682158cabf646",
"value": "1083361cc611227501b682158cabf646"
},
"made_sla": "false",
"notify": "1",
"number": "INC0000001",
"opened_at": "2018-02-20 23:09:51",
"opened_by": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user/681ccaf9c0a8016400b98a06818d57c7",
"value": "681ccaf9c0a8016400b98a06818d57c7"
},
"order": "",
"parent": "",
"parent_incident": "",
"priority": "1",
"problem_id": {
"link": "https://dev56245.service-now.com/api/now/table/problem/9d3a266ac6112287004e37fb2ceb0133",
"value": "9d3a266ac6112287004e37fb2ceb0133"
},
"reassignment_count": "1",
"reopen_count": "",
"reopened_by": "",
"reopened_time": "",
"resolved_at": "2018-05-23 19:56:12",
"resolved_by": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user/6816f79cc0a8016401c5a33be04be441",
"value": "6816f79cc0a8016401c5a33be04be441"
},
"rfc": "",
"severity": "1",
"short_description": "Can't read email",
"sla_due": "",
"state": "7",
"subcategory": "",
"sys_class_name": "incident",
"sys_created_by": "pat",
"sys_created_on": "2016-09-21 18:24:13",
"sys_domain": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user_group/global",
"value": "global"
},
"sys_domain_path": "/",
"sys_id": "9c573169c611228700193229fff72400",
"sys_mod_count": "21",
"sys_tags": "",
"sys_updated_by": "admin",
"sys_updated_on": "2018-05-23 20:16:07",
"time_worked": "",
"u_test": "",
"upon_approval": "",
"upon_reject": "",
"urgency": "1",
"user_input": "",
"watch_list": "",
"work_end": "",
"work_notes": "",
"work_notes_list": "",
"work_start": ""
},
{
"active": "true",
"activity_due": "2018-05-23 21:51:11",
"additional_assignee_list": "",
"approval": "",
"approval_history": "",
"approval_set": "",
"assigned_to": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user/46ca0887a9fe19810191e08e51927ebf",
"value": "46ca0887a9fe19810191e08e51927ebf"
},
"assignment_group": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user_group/287ebd7da9fe198100f92cc8d1d2154e",
"value": "287ebd7da9fe198100f92cc8d1d2154e"
},
"business_duration": "",
"business_service": "",
"business_stc": "",
"calendar_duration": "",
"calendar_stc": "",
"caller_id": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user/5137153cc611227c000bbd1bd8cd2005",
"value": "5137153cc611227c000bbd1bd8cd2005"
},
"category": "network",
"caused_by": "",
"child_incidents": "",
"close_code": "",
"close_notes": "",
"closed_at": "",
"closed_by": "",
"cmdb_ci": {
"link": "https://dev56245.service-now.com/api/now/table/cmdb_ci/b0c25d1bc0a800090168be1bfcdcd759",
"value": "b0c25d1bc0a800090168be1bfcdcd759"
},
"comments": "",
"comments_and_work_notes": "",
"company": "",
"contact_type": "",
"correlation_display": "",
"correlation_id": "",
"delivery_plan": "",
"delivery_task": "",
"description": "User can't get to any of his files on the file server.",
"due_date": "",
"escalation": "3",
"expected_start": "",
"follow_up": "",
"group_list": "",
"hold_reason": "4",
"impact": "1",
"incident_state": "3",
"knowledge": "false",
"location": {
"link": "https://dev56245.service-now.com/api/now/table/cmn_location/108486c7c611227500b093211aa88dcc",
"value": "108486c7c611227500b093211aa88dcc"
},
"made_sla": "false",
"notify": "1",
"number": "INC0000002",
"opened_at": "2018-02-14 23:07:12",
"opened_by": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user/681ccaf9c0a8016400b98a06818d57c7",
"value": "681ccaf9c0a8016400b98a06818d57c7"
},
"order": "",
"parent": "",
"parent_incident": "",
"priority": "1",
"problem_id": {
"link": "https://dev56245.service-now.com/api/now/table/problem/9d3a266ac6112287004e37fb2ceb0133",
"value": "9d3a266ac6112287004e37fb2ceb0133"
},
"reassignment_count": "1",
"reopen_count": "",
"reopened_by": "",
"reopened_time": "",
"resolved_at": "",
"resolved_by": "",
"rfc": "",
"severity": "1",
"short_description": "Network file shares access issue",
"sla_due": "",
"state": "3",
"subcategory": "",
"sys_class_name": "incident",
"sys_created_by": "pat",
"sys_created_on": "2016-09-10 22:30:06",
"sys_domain": {
"link": "https://dev56245.service-now.com/api/now/table/sys_user_group/global",
"value": "global"
},
"sys_domain_path": "/",
"sys_id": "9d385017c611228701d22104cc95c371",
"sys_mod_count": "17",
"sys_tags": "",
"sys_updated_by": "admin",
"sys_updated_on": "2018-05-23 19:51:11",
"time_worked": "",
"u_test": "",
"upon_approval": "",
"upon_reject": "",
"urgency": "1",
"user_input": "",
"watch_list": "",
"work_end": "",
"work_notes": "",
"work_notes_list": "",
"work_start": ""
}
]
}
Commands
You can execute these commands from the Cortex XSOAR 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.
- Get ticket information: servicenow-get-ticket
- Create a ticket: servicenow-create-ticket
- Update a ticket: servicenow-update-ticket
- Delete a ticket: servicenow-delete-ticket
- Add a link to a ticket: servicenow-add-link
- Add a comment to a ticket: servicenow-add-comment
- Get ticket information for a query: servicenow-query-tickets
- Upload a file to a ticket: servicenow-upload-file
- Get record information: servicenow-get-record
- Query a table: servicenow-query-table
- Create a record in a table: servicenow-create-record
- Update a record in a table: servicenow-update-record
- Delete a record from a table: servicenow-delete-record
- List API fields for a table: servicenow-list-table-fields
- Query computers: servicenow-query-computers
- Query groups: servicenow-query-groups
- Query users: servicenow-query-users
- Get table names: servicenow-get-table-name
- Get ticket notes: servicenow-get-ticket-notes
1. Get ticket information
Retrieves ticket information by the ticket ID.
Base Command
servicenow-get-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| id | Ticket system ID to retrieve | Optional |
| ticket_type | Ticket type | Optional |
| number | Ticket number to retrieve | Optional |
| get_attachments | Whether to retrieve ticket attachments, default is false | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Ticket.ID | string | ServiceNow ticket ID |
| ServiceNow.Ticket.OpenedBy | string | ServiceNow ticket opener ID |
| ServiceNow.Ticket.CreatedOn | date | ServiceNow ticket creation date |
| ServiceNow.Ticket.Assignee | string | ServiceNow ticket assignee ID |
| ServiceNow.Ticket.State | string | ServiceNow ticket state |
| ServiceNow.Ticket.Summary | string | ServiceNow ticket short summary |
| ServiceNow.Ticket.Number | string | ServiceNow ticket number |
| ServiceNow.Ticket.Active | boolean | ServiceNow ticket active |
| ServiceNow.Ticket.AdditionalComments | string | ServiceNow ticket comments |
| ServiceNow.Ticket.Priority | string | ServiceNow ticket priority |
| ServiceNow.Ticket.OpenedAt | date | ServiceNow ticket opening time |
| ServiceNow.Ticket.ResolvedBy | string | ServiceNow ticket resolver ID |
| ServiceNow.Ticket.CloseCode | string | ServiceNow ticket close code |
| File.Info | string | Attachment file info |
| File.Name | string | Attachment file name |
| File.Size | number | Attachment file size |
| File.SHA1 | string | Attachment file SHA-1 |
| File.SHA256 | string | Attachment file SHA-256 |
| File.EntryID | string | Attachment file entry ID |
| File.Type | string | Attachment file type |
| File.MD5 | string | Attachment file MD5 |
Command Example
!servicenow-get-ticket number=INC0010052
Context Example
{
"ServiceNow": {
"Ticket": {
"Active": "true",
"CreatedOn": "2018-09-04 08:21:33",
"ID": "0c23f8d24f102300d316b63ca310c742",
"Number": "INC0010052",
"OpenedAt": "2018-09-04 08:21:33",
"OpenedBy": "6816f79cc0a8016401c5a33be04be441",
"Priority": "4 - Low",
"State": "1 - New",
"Summary": "This is a new ticket"
}
}
}
Human Readable Output
2. Create a ticket
Creates a new ServiceNow ticket.
Base Command
servicenow-create-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| short_description | Short description of the ticket | Optional |
| ticket_type | Ticket type | Optional |
| urgency | Ticket urgency | Optional |
| severity | Ticket severity | Optional |
| impact | Ticket impact | Optional |
| active | Set ticket as Active | Optional |
| activity_due | Set ticket ActivityDue - format "2016-07-02 21:51:11" | Optional |
| additional_assignee_list | List of assigned users to the ticket | Optional |
| approval_history | Ticket history approval | Optional |
| approval_set | Set ticket ApprovalSet - format "2016-07-02 21:51:11" | Optional |
| assigned_to | To whom the ticket is assigned | Optional |
| business_duration | Format: YYYY-MM-DD HH:MM:SS | Optional |
| business_service | Business service | Optional |
| business_stc | Business source | Optional |
| calendar_duration | Format: YYYY-MM-DD HH:MM:SS | Optional |
| caller_id | UID Format | Optional |
| category | Category of ticket | Optional |
| caused_by | UID Format | Optional |
| close_code | Ticket's close code | Optional |
| close_notes | Close notes of the ticket | Optional |
| closed_at | Format: YYYY-MM-DD HH:MM:SS | Optional |
| closed_by | User who closed the ticket | Optional |
| cmdb_ci | UID Format | Optional |
| comments | Format type journal input | Optional |
| comments_and_work_notes | Format type journal input | Optional |
| company | UID Format | Optional |
| contact_type | Contact type | Optional |
| correlation_display | Correlation display | Optional |
| correlation_id | Correlation id | Optional |
| delivery_plan | UID Format | Optional |
| display | If you want to display comments, work_notes... | Optional |
| description | Ticket description | Optional |
| due_date | Format: YYYY-MM-DD HH:MM:SS | Optional |
| escalation | Escalation | Optional |
| expected_start | Format: YYYY-MM-DD HH:MM:SS | Optional |
| follow_up | Format: YYYY-MM-DD HH:MM:SS | Optional |
| group_list | UID format list | Optional |
| knowledge | Is the ticket solved in the knowledge base | Optional |
| location | Location of the ticket | Optional |
| made_sla | SLA of the ticket | Optional |
| notify | Notify about this ticket | Optional |
| order | Order number | Optional |
| parent | UID Format | Optional |
| parent_incident | UID Format | Optional |
| problem_id | UID Format | Optional |
| reassignment_count | How many users included in this ticket before | Optional |
| reopen_count | How many time the ticket has been reopened | Optional |
| resolved_at | Format: YYYY-MM-DD HH:MM:SS | Optional |
| resolved_by | UID Format | Optional |
| rfc | UID | Optional |
| sla_due | Format: YYYY-MM-DD HH:MM:SS | Optional |
| subcategory | Subcategory | Optional |
| sys_updated_by | Last updated by | Optional |
| sys_updated_on | Format: YYYY-MM-DD HH:MM:SS | Optional |
| user_input | Input from the end user | Optional |
| watch_list | A list of watched tickets | Optional |
| work_end | Format: YYYY-MM-DD HH:MM:SS | Optional |
| work_notes | Format journal list | Optional |
| work_notes_list | List with UIDs | Optional |
| work_start | Date when started to work on the ticket | Optional |
| assignment_group | Set AssignmentGroup - sys_id of group | Optional |
| incident_state | integer | Optional |
| number | Ticket number | Optional |
| priority | Priority of the ticket | Optional |
| template | Template name to use as a base to create new tickets. | Optional |
| custom_fields | Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;... | Optional |
| change_type | Type of change request ticket | Optional |
| state | State of the ticket, e.g., "Closed" or "7" or "7 - Closed | Optional |
| opened_at | Time the ticket was opened (YYYY-MM-DD HH:MM:SS) | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Ticket.ID | string | ServiceNow ticket ID |
| ServiceNow.Ticket.OpenedBy | string | ServiceNow ticket opener ID |
| ServiceNow.Ticket.CreatedOn | date | ServiceNow ticket creation date |
| ServiceNow.Ticket.Assignee | string | ServiceNow ticket assignee ID |
| ServiceNow.Ticket.State | string | ServiceNow ticket state |
| ServiceNow.Ticket.Summary | string | ServiceNow ticket short summary |
| ServiceNow.Ticket.Number | string | ServiceNow ticket number |
| ServiceNow.Ticket.Active | boolean | ServiceNow ticket active |
| ServiceNow.Ticket.AdditionalComments | string | ServiceNow ticket comments |
| ServiceNow.Ticket.Priority | string | ServiceNow ticket priority |
| ServiceNow.Ticket.OpenedAt | date | ServiceNow ticket opening time |
| ServiceNow.Ticket.ResolvedBy | string | ServiceNow ticket resolver ID |
| ServiceNow.Ticket.CloseCode | string | ServiceNow ticket close code |
Command Example
!servicenow-create short_description="This is a new ticket" ticket_type="incident" type="normal"
Context Example
{
"ServiceNow": {
"Ticket": {
"Active": "true",
"CreatedOn": "2018-11-27 12:11:39",
"ID": "bd5d42994f82230021ae045e9310c7bd",
"Number": "INC0010449",
"OpenedAt": "2018-11-27 12:11:39",
"OpenedBy": "6816f79cc0a8016401c5a33be04be441",
"Priority": "5 - Planning",
"State": "1 - New",
"Summary": "This is a new ticket"
}
}
}
Human Readable Output
3. Update a ticket
Updates a specified ServiceNow ticket.
Base Command
servicenow-update-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| short_description | Short description of the ticket | Optional |
| ticket_type | Ticket type | Optional |
| urgency | Ticket urgency | Optional |
| severity | Ticket severity | Optional |
| impact | Ticket impact | Optional |
| active | Does the ticket active (true/false) | Optional |
| activity_due | Format: YYYY-MM-DD HH:MM:SS | Optional |
| additional_assignee_list | List of assigned users to the ticket | Optional |
| approval_history | Ticket history approval | Optional |
| approval_set | Set ticket ApprovalSet - format "2016-07-02 21:51:11" | Optional |
| assigned_to | To whom the ticket is assigned | Optional |
| business_duration | Format: YYYY-MM-DD HH:MM:SS | Optional |
| business_service | Business service | Optional |
| business_stc | Business source | Optional |
| calendar_duration | Format: YYYY-MM-DD HH:MM:SS | Optional |
| caller_id | UID Format | Optional |
| category | Category name | Optional |
| caused_by | UID Format | Optional |
| close_code | Ticket's close code | Optional |
| close_notes | Close notes of the ticket | Optional |
| closed_at | Format: YYYY-MM-DD HH:MM:SS | Optional |
| closed_by | User who closed the ticket | Optional |
| cmdb_ci | UID Format | Optional |
| comments | Format type journal input | Optional |
| comments_and_work_notes | Format type journal input | Optional |
| company | UID Format | Optional |
| contact_type | Contact type | Optional |
| correlation_display | Correlation display | Optional |
| correlation_id | Correlation id | Optional |
| delivery_plan | UID Format | Optional |
| display | If you want to display comments, work_notes... | Optional |
| description | Ticket description | Optional |
| due_date | Format: YYYY-MM-DD HH:MM:SS | Optional |
| escalation | Escalation | Optional |
| expected_start | Format: YYYY-MM-DD HH:MM:SS | Optional |
| follow_up | Format: YYYY-MM-DD HH:MM:SS | Optional |
| group_list | UID format list | Optional |
| knowledge | Is the ticket solved in the knowledge base | Optional |
| location | Location of the ticket | Optional |
| made_sla | SLA of the ticket | Optional |
| notify | Notify about this ticket | Optional |
| order | Order number | Optional |
| parent | UID Format | Optional |
| parent_incident | UID Format | Optional |
| problem_id | UID Format | Optional |
| reassignment_count | How many users included in this ticket before | Optional |
| reopen_count | How many time the ticket has been reopened | Optional |
| resolved_at | Format: YYYY-MM-DD HH:MM:SS | Optional |
| resolved_by | UID Format | Optional |
| rfc | UID | Optional |
| sla_due | Format: YYYY-MM-DD HH:MM:SS | Optional |
| subcategory | Subcategory | Optional |
| sys_updated_by | Last updated by | Optional |
| sys_updated_on | Format: YYYY-MM-DD HH:MM:SS | Optional |
| user_input | Input from the end user | Optional |
| watch_list | A list of watched tickets | Optional |
| work_end | Format: YYYY-MM-DD HH:MM:SS | Optional |
| work_notes | Format journal list | Optional |
| work_notes_list | List with UIDs | Optional |
| work_start | Date when started to work on the ticket | Optional |
| assignment_group | UID | Optional |
| incident_state | integer | Optional |
| number | Ticket number | Optional |
| priority | Priority of the ticket | Optional |
| id | System ID of the ticket to update | Required |
| custom_fields | Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;... | Optional |
| change_type | Type of Change Request ticket | Optional |
| state | State of the ticket, e.g., "Closed" or "7" or "7 - Closed | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Ticket.ID | string | ServiceNow ticket ID |
| ServiceNow.Ticket.OpenedBy | string | ServiceNow ticket opener ID |
| ServiceNow.Ticket.CreatedOn | date | ServiceNow ticket creation date |
| ServiceNow.Ticket.Assignee | string | ServiceNow ticket assignee ID |
| ServiceNow.Ticket.State | string | ServiceNow ticket state |
| ServiceNow.Ticket.Summary | string | ServiceNow ticket short summary |
| ServiceNow.Ticket.Number | string | ServiceNow ticket number |
| ServiceNow.Ticket.Active | boolean | ServiceNow ticket active |
| ServiceNow.Ticket.AdditionalComments | string | ServiceNow ticket comments |
| ServiceNow.Ticket.Priority | string | ServiceNow ticket priority |
| ServiceNow.Ticket.OpenedAt | date | ServiceNow ticket opening time |
| ServiceNow.Ticket.ResolvedBy | string | ServiceNow ticket resolver ID |
| ServiceNow.Ticket.CloseCode | string | ServiceNow ticket close code |
Command Example
!servicenow-update ticket_type="incident" impact="2" id="c34852514fc2230021ae045e9310c7ab"
Context Example
{
"ServiceNow": {
"Ticket": {
"Active": "true",
"CreatedOn": "2018-11-27 12:59:32",
"ID": "c34852514fc2230021ae045e9310c7ab",
"Number": "INC0010452",
"OpenedAt": "2018-11-27 12:59:32",
"OpenedBy": "6816f79cc0a8016401c5a33be04be441",
"Priority": "4 - Low",
"State": "1 - New"
}
}
}
Human Readable Output
4. Delete a ticket
Deletes a specified ServiceNow ticket.
Base Command
servicenow-delete-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| id | Ticket system ID | Required |
| ticket_type | Ticket type | Optional |
Context Output
There is no context output for this command.
Command Example
!servicenow-delete-ticket id=0c23f8d24f102300d316b63ca310c742
Human Readable Output
5. Add a link to a ticket
Adds a link to a specified ServiceNow ticket.
Base Command
servicenow-add-link
Input
| Argument Name | Description | Required |
|---|---|---|
| id | Ticket system ID | Required |
| ticket_type | Ticket type | Optional |
| link | The actual link to publish in ServiceNow ticket, valid url format, like http://www.demisto.com | Required |
| post-as-comment | Publish the link as comment on the ticket, if false will publish the link as WorkNote, format boolean | Optional |
| text | The text to represent the link | Optional |
Context Output
There is no context output for this command.
Command Example
!servicenow-add-link id="bd5d42994f82230021ae045e9310c7bd" ticket_type="incident" link="www.demisto.com" text="this is a link"
Human Readable Output
6. Add a comment to a ticket
Adds a comment to a specified ticket.
Base Command
servicenow-add-comment
Input
| Argument Name | Description | Required |
|---|---|---|
| id | Ticket system ID | Required |
| ticket_type | Ticket type | Optional |
| comment | Comment to add | Required |
| post-as-comment | Publish the link as a comment on the ticket, if false will publish the link as WorkNote, format boolean | Optional |
Context Output
There is no context output for this command.
Command Example
!servicenow-add-comment id="0c23f8d24f102300d316b63ca310c742" ticket_type="incident" comment="This is a comment"
Human Readable Output
7. Get ticket information from a query
Retrieves ticket information via a query.
Base Command
servicenow-query-tickets
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | Limit for how many tickets to retrieve | Optional |
| ticket_type | Ticket type | Optional |
| query | The query to run. To learn about querying in ServiceNow, see the ServiceNow documentation. | Optional |
| offset | Starting record index to begin retrieving records from | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Ticket.ID | string | The unique ticket identifier |
| Ticket.Creator | string | A string field that indicates the user who created the ticket |
| Ticket.CreatedOn | date | The date and time when the ticket was created |
| Ticket.Assignee | string | Specifies the user assigned to complete the ticket. By default, this field uses a reference qualifier to only display users with the itil role. |
| Ticket.State | string | Status of the ticket |
| Ticket.Summary | string | A human-readable title for the record |
| Ticket.Number | string | The display value of the ticket |
| Ticket.Active | boolean | Specifies whether work is still being done on a task or whether the work for the task is complete |
| Ticket.AdditionalComments | unknown | Comments about the task record |
| Ticket.Priority | string | Specifies how high a priority the ticket should be for the assignee |
| Ticket.OpenedAt | date | The date and time when the ticket was opened for the first time |
| Ticket.Escalation | string | Indicates how long the ticket has been open |
Command Example
!servicenow-query-tickets limit="10" query="impact<2^short_descriptionISNOTEMPTY" ticket_type="incident"
Context Example
{
"ServiceNow": {
"Ticket": [
{
"Active": "false",
"Assignee": "46b87022a9fe198101a78787e40d7547",
"CloseCode": "Closed/Resolved by Caller",
"CreatedOn": "2016-09-21 18:24:13",
"ID": "9c573169c611228700193229fff72400",
"Number": "INC0000001",
"OpenedAt": "2018-02-20 23:09:51",
"OpenedBy": "681ccaf9c0a8016400b98a06818d57c7",
"Priority": "1 - Critical",
"ResolvedBy": "9ee1b13dc6112271007f9d0efdb69cd0",
"State": "7 - Closed",
"Summary": "Can't read email"
},
{
"Active": "true",
"Assignee": "46ca0887a9fe19810191e08e51927ebf",
"CreatedOn": "2016-09-10 22:30:06",
"ID": "9d385017c611228701d22104cc95c371",
"Number": "INC0000002",
"OpenedAt": "2018-02-14 23:07:12",
"OpenedBy": "681ccaf9c0a8016400b98a06818d57c7",
"Priority": "1 - Critical",
"State": "3 - On Hold",
"Summary": "Network file shares access issue"
},
{
"Active": "true",
"Assignee": "46d44a23a9fe19810012d100cca80666",
"CreatedOn": "2016-09-25 14:41:46",
"ID": "e8caedcbc0a80164017df472f39eaed1",
"Number": "INC0000003",
"OpenedAt": "2018-02-21 23:07:30",
"OpenedBy": "681b365ec0a80164000fb0b05854a0cd",
"Priority": "1 - Critical",
"State": "2 - In Progress",
"Summary": "Wireless access is down in my area"
},
{
"Active": "false",
"Assignee": "46c6f9efa9fe198101ddf5eed9adf6e7",
"CloseCode": "Solved (Work Around)",
"CreatedOn": "2016-09-21 22:34:12",
"ID": "9d3c1197c611228701cd1d94bc32d76d",
"Number": "INC0000004",
"OpenedAt": "2018-02-27 22:49:22",
"OpenedBy": "681b365ec0a80164000fb0b05854a0cd",
"Priority": "1 - Critical",
"ResolvedBy": "9ee1b13dc6112271007f9d0efdb69cd0",
"State": "7 - Closed",
"Summary": "Forgot email password"
},
{
"Active": "false",
"Assignee": "46c6f9efa9fe198101ddf5eed9adf6e7",
"CloseCode": "Closed/Resolved by Caller",
"CreatedOn": "2016-09-25 15:14:01",
"ID": "e8e875b0c0a80164009dc852b4d677d5",
"Number": "INC0000005",
"OpenedAt": "2018-02-16 23:06:52",
"OpenedBy": "6816f79cc0a8016401c5a33be04be441",
"Priority": "1 - Critical",
"ResolvedBy": "6816f79cc0a8016401c5a33be04be441",
"State": "7 - Closed",
"Summary": "CPU load high for over 10 minutes"
},
{
"Active": "false",
"Assignee": "46ca0887a9fe19810191e08e51927ebf",
"CloseCode": "Closed/Resolved by Caller",
"CreatedOn": "2016-09-18 20:42:01",
"ID": "8d6246c7c0a80164012fb063cecd4ace",
"Number": "INC0000006",
"OpenedAt": "2018-02-20 23:08:05",
"OpenedBy": "5136503cc611227c0183e96598c4f706",
"Priority": "1 - Critical",
"ResolvedBy": "9ee1b13dc6112271007f9d0efdb69cd0",
"State": "7 - Closed",
"Summary": "Hangs when trying to print VISIO document"
},
{
"Active": "true",
"Assignee": "5137153cc611227c000bbd1bd8cd2007",
"CreatedOn": "2014-03-10 20:43:10",
"ID": "8d6353eac0a8016400d8a125ca14fc1f",
"Number": "INC0000007",
"OpenedAt": "2015-08-12 23:08:24",
"OpenedBy": "681ccaf9c0a8016400b98a06818d57c7",
"Priority": "1 - Critical",
"State": "3 - On Hold",
"Summary": "Need access to sales DB for the West"
},
{
"Active": "false",
"Assignee": "681b365ec0a80164000fb0b05854a0cd",
"CloseCode": "Closed/Resolved by Caller",
"CreatedOn": "2016-09-18 20:43:58",
"ID": "8d641046c0a80164000bc7c0d3ed46a0",
"Number": "INC0000008",
"OpenedAt": "2018-02-28 23:08:39",
"OpenedBy": "681ccaf9c0a8016400b98a06818d57c7",
"Priority": "1 - Critical",
"ResolvedBy": "9ee1b13dc6112271007f9d0efdb69cd0",
"State": "7 - Closed",
"Summary": "Printer in my office is out of toner"
},
{
"Active": "false",
"Assignee": "5137153cc611227c000bbd1bd8cd2007",
"CloseCode": "Closed/Resolved by Caller",
"CreatedOn": "2018-03-05 22:51:33",
"ID": "46b66a40a9fe198101f243dfbc79033d",
"Number": "INC0000009",
"OpenedAt": "2018-02-27 22:50:23",
"OpenedBy": "9ee1b13dc6112271007f9d0efdb69cd0",
"Priority": "1 - Critical",
"ResolvedBy": "9ee1b13dc6112271007f9d0efdb69cd0",
"State": "7 - Closed",
"Summary": "Reset my password"
},
{
"Active": "false",
"Assignee": "5137153cc611227c000bbd1bd8cd2007",
"CloseCode": "Solved (Work Around)",
"CreatedOn": "2018-03-05 23:18:07",
"ID": "46cebb88a9fe198101aee93734f9768b",
"Number": "INC0000013",
"OpenedAt": "2018-03-04 23:15:58",
"OpenedBy": "9ee1b13dc6112271007f9d0efdb69cd0",
"Priority": "1 - Critical",
"ResolvedBy": "9ee1b13dc6112271007f9d0efdb69cd0",
"State": "7 - Closed",
"Summary": "EMAIL is slow when an attachment is involved"
}
]
}
}
Human Readable Output
8. Upload a file to a ticket
Uploads a file to a specified ServiceNow ticket.
Base Command
servicenow-upload-file
Input
| Argument Name | Description | Required |
|---|---|---|
| id | Ticket system ID | Required |
| ticket_type | Ticket type | Optional |
| file_id | War Room entry ID that includes the file | Required |
| file_name | File name of uploaded file to override the existing file name in entry | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Ticket.File.Filename | string | Name of the file |
| Ticket.File.Link | string | Download link for the file |
| Ticket.File.SystemID | string | System ID of the file |
Command Example
!servicenow-upload-file file_id=8781@cc00e449-9e7b-4609-8a68-1c8c01114562 id=bd5d42994f82230021ae045e9310c7bd
Context Example
{
"File": {
"EntryID": "8786@cc00e449-9e7b-4609-8a68-1c8c01114562",
"Extension": "txt",
"Info": "text/plain; charset=utf-8",
"MD5": "ea499981fdaadbd1a26250b0d925bf43",
"Name": "file.txt",
"SHA1": "7ca8be259038fbc1c10ab953363114f3e222f842",
"SHA256": "02790f5a87eaa10abf8023268ea576d7a72acaab77e0a8371e44491b357728bc",
"SSDeep": "384:fBr9CRIuXUA+m0x/nL8LuyahOMKFE3qw3db27XUS354AI3NUrXG:5r9i0xfL8LuyahOMK+3z3db2L354vdiW",
"Size": 51080,
"Type": "ASCII text\n"
},
"ServiceNow": {
"Ticket": {
"File": {
"Filename": "file.txt",
"Link": "https://dev56245.service-now.com/api/now/attachment/8cc29e1d4f82230021ae045e9310c723/file",
"SystemID": "8cc29e1d4f82230021ae045e9310c723"
},
"ID": "bd5d42994f82230021ae045e9310c7bd"
}
}
}
Human Readable Output
9. Get record information
Retrieves information for a specified record.
Base Command
servicenow-get-record
Input
| Argument Name | Description | Required |
|---|---|---|
| id | Record system ID | Required |
| fields | Comma-separated table fields to display and output to the context, e.g., name,tag,company. ID field is added by default. | Optional |
| table_name | The name of the table to get the record from | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Record.ID | string | The unique record identifier for the record |
| ServiceNow.Record.UpdatedBy | string | A string field that indicates the user who most recently updated the record |
| ServiceNow.Record.UpdatedAt | date | A time-stamp field that indicates the date and time of the most recent update |
| ServiceNow.Record.CreatedBy | string | A string field that indicates the user who created the record |
| ServiceNow.Record.CreatedOn | date | A time-stamp field that indicates when a record was created |
Command Example
!servicenow-get-record table_name=alm_asset id=00a96c0d3790200044e0bfc8bcbe5dc3 fields=asset_tag,sys_updated_by,display_name
Context Example
{
"ServiceNow": {
"Record": {
"ID": "00a96c0d3790200044e0bfc8bcbe5dc3",
"asset_tag": "P1000479",
"display_name": "P1000479 - Apple MacBook Pro 15\"",
"sys_updated_by": "system"
}
}
}
Human Readable Output
10. Query a table
Queries a specified table in ServiceNow.
Base Command
servicenow-query-table
Input
| Argument Name | Description | Required |
|---|---|---|
| table_name | The name of the table to query | Required |
| limit | Limit for how many tickets to retrieve | Optional |
| query | The query to run. For more information about querying in ServiceNow, see the ServiceNow documentation. | Optional |
| fields | Comma-separated table fields to display and output to the context, e.g., name,tag,company. ID field is added by default. | Optional |
| offset | Starting record index to begin retrieving records from | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Results.ID | string | The unique record identifier for the record |
| ServiceNow.Results.UpdatedBy | string | A string field that indicates the user who most recently updated the record |
| ServiceNow.Results.UpdatedAt | date | A time-stamp field that indicates the date and time of the most recent update |
| ServiceNow.Results.CreatedBy | string | A string field that indicates the user who created the record |
| ServiceNow.Results.CreatedOn | date | A time-stamp field that indicates when a record was created |
Command Example
!servicenow-query-table table_name=alm_asset fields=asset_tag,sys_updated_by,display_name query=display_nameCONTAINSMacBook
Context Example
{
"ServiceNow": {
"Record": [
{
"ID": "00a96c0d3790200044e0bfc8bcbe5dc3",
"asset_tag": "P1000479",
"display_name": "P1000479 - Apple MacBook Pro 15\"",
"sys_updated_by": "system"
},
{
"ID": "01a92c0d3790200044e0bfc8bcbe5d36",
"asset_tag": "P1000807",
"display_name": "P1000807 - Apple MacBook Pro 17\"",
"sys_updated_by": "system"
},
{
"ID": "01a9a80d3790200044e0bfc8bcbe5d53",
"asset_tag": "P1000637",
"display_name": "P1000637 - Apple MacBook Air 13\"",
"sys_updated_by": "system"
},
{
"ID": "02a9a80d3790200044e0bfc8bcbe5dba",
"asset_tag": "P1000412",
"display_name": "P1000412 - Apple MacBook Pro 17\"",
"sys_updated_by": "system"
},
{
"ID": "02a9ac0d3790200044e0bfc8bcbe5d5e",
"asset_tag": "P1000563",
"display_name": "P1000563 - Apple MacBook Pro 15\"",
"sys_updated_by": "system"
},
{
"ID": "03a9680d3790200044e0bfc8bcbe5d4c",
"asset_tag": "P1000626",
"display_name": "P1000626 - Apple MacBook Air 13\"",
"sys_updated_by": "system"
},
{
"ID": "03a9ac0d3790200044e0bfc8bcbe5d9d",
"asset_tag": "P1000551",
"display_name": "P1000551 - Apple MacBook Pro 15\"",
"sys_updated_by": "system"
},
{
"ID": "04a96c0d3790200044e0bfc8bcbe5db3",
"asset_tag": "P1000503",
"display_name": "P1000503 - Apple MacBook Pro 15\"",
"sys_updated_by": "system"
},
{
"ID": "04a9a80d3790200044e0bfc8bcbe5d1b",
"asset_tag": "P1000640",
"display_name": "P1000640 - Apple MacBook Air 13\"",
"sys_updated_by": "system"
},
{
"ID": "04a9e80d3790200044e0bfc8bcbe5d41",
"asset_tag": "P1000815",
"display_name": "P1000815 - Apple MacBook Pro 17\"",
"sys_updated_by": "system"
}
]
}
}
Human Readable Output
11. Create a record in a table
Creates a new record in a specified ServiceNow table.
Base Command
servicenow-create-record
Input
| Argument Name | Description | Required |
|---|---|---|
| table_name | The name of the table to create a record in | Required |
| fields | Fields and their values to create the record with, in the format: fieldname1=value;fieldname2=value;... | Optional |
| custom_fields | Custom(user defined) fields in the format: fieldname1=value;fieldname2=value;... | Optional |
| template | Template name to use as a base to create new records | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Record.ID | string | The unique record identifier for the record |
| ServiceNow.Record.UpdatedBy | string | A string field that indicates the user who most recently updated the record |
| ServiceNow.Record.UpdatedAt | date | A time-stamp field that indicates the date and time of the most recent update |
| ServiceNow.Record.CreatedBy | string | A string field that indicates the user who created the record |
| ServiceNow.Record.CreatedOn | date | time-stamp field that indicates when a record was created |
Command Example
!servicenow-create-record table_name=incident fields="short_description=My description;assigned_to=my user"
Context Example
{
"ServiceNow": {
"Record": {
"CreatedAt": "2018-11-27 12:51:44",
"CreatedBy": "admin",
"ID": "748692114fc2230021ae045e9310c7ff",
"UpdatedAt": "2018-11-27 12:51:44",
"UpdatedBy": "admin"
}
}
}
Human Readable Output
12. Update a record in a table
Updates a record in a specified ServiceNow table.
Base Command
servicenow-update-record
Input
| Argument Name | Description | Required |
|---|---|---|
| table_name | The name of the table to update the record in | Required |
| id | The system ID of the ticket to update | Required |
| fields | Fields and their values to update in the record, in the format: fieldname1=value;fieldname2=value;... | Optional |
| custom_fields | Custom (user-defined) fields and their values to update in the record, in the format: fieldname1=value;fieldname2=value;... | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Record.ID | string | The unique record identifier for the record. |
| ServiceNow.Record.UpdatedBy | string | A string field that indicates the user who most recently updated the record. |
| ServiceNow.Record.UpdatedAt | date | A time-stamp field that indicates the date and time of the most recent update. |
| ServiceNow.Record.CreatedBy | string | A string field that indicates the user who created the record. |
| ServiceNow.Record.CreatedOn | date | time-stamp field that indicates when a record was created. |
Command Example
!servicenow-update-record table_name=incident id=748692114fc2230021ae045e9310c7ff fields="short_description=new desc;assigned_to=new user"
Context Example
{
"ServiceNow": {
"Record": {
"CreatedAt": "2018-11-27 12:51:44",
"CreatedBy": "admin",
"ID": "748692114fc2230021ae045e9310c7ff",
"UpdatedAt": "2018-11-27 12:53:48",
"UpdatedBy": "admin"
}
}
}
Human Readable Output
13. Delete a record from a table
Deletes a record from a specified ServiceNow table.
Base Command
servicenow-delete-record
Input
| Argument Name | Description | Required |
|---|---|---|
| table_name | The table name | Required |
| id | The system ID of the ticket to delete | Required |
Context Output
There is no context output for this command.
Command Example
!servicenow-delete-record id=748692114fc2230021ae045e9310c7ff table_name=incident
Human Readable Output
14. List API fiels for a table
Lists API fields for a specified ServiceNow table.
Base Command
servicenow-list-table-fields
Input
| Argument Name | Description | Required |
|---|---|---|
| table_name | Table name | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Field | string | Table API field name |
Command Example
!servicenow-list-table-fields table_name=alm_asset
Context Example
{
"ServiceNow": {
"Field": [
{
"Name": "reserved_for"
},
{
"Name": "expenditure_type"
},
{
"Name": "request_line"
},
{
"Name": "checked_in"
},
{
"Name": "sys_tags"
},
{
"Name": "sys_created_on"
},
{
"Name": "gl_account"
},
{
"Name": "salvage_value"
},
{
"Name": "ci"
},
{
"Name": "asset_tag"
},
{
"Name": "managed_by"
},
{
"Name": "assigned_to"
},
{
"Name": "install_status"
},
{
"Name": "cost_center"
},
{
"Name": "old_substatus"
},
{
"Name": "pre_allocated"
},
{
"Name": "display_name"
},
{
"Name": "justification"
},
{
"Name": "skip_sync"
},
{
"Name": "sys_updated_by"
},
{
"Name": "sys_created_by"
},
{
"Name": "due"
},
{
"Name": "comments"
},
{
"Name": "beneficiary"
},
{
"Name": "depreciated_amount"
},
{
"Name": "lease_id"
},
{
"Name": "sys_id"
},
{
"Name": "purchase_date"
},
{
"Name": "work_notes"
},
{
"Name": "department"
},
{
"Name": "serial_number"
},
{
"Name": "location"
},
{
"Name": "residual_date"
},
{
"Name": "sys_class_name"
},
{
"Name": "warranty_expiration"
},
{
"Name": "vendor"
},
{
"Name": "parent"
},
{
"Name": "checked_out"
},
{
"Name": "company"
},
{
"Name": "sys_updated_on"
},
{
"Name": "resale_price"
},
{
"Name": "residual"
},
{
"Name": "supported_by"
},
{
"Name": "sys_mod_count"
},
{
"Name": "disposal_reason"
},
{
"Name": "invoice_number"
},
{
"Name": "old_status"
},
{
"Name": "sys_domain_path"
},
{
"Name": "retired"
},
{
"Name": "assigned"
},
{
"Name": "depreciation"
},
{
"Name": "model_category"
},
{
"Name": "po_number"
},
{
"Name": "acquisition_method"
},
{
"Name": "due_in"
},
{
"Name": "depreciation_date"
},
{
"Name": "install_date"
},
{
"Name": "substatus"
},
{
"Name": "order_date"
},
{
"Name": "cost"
},
{
"Name": "support_group"
},
{
"Name": "stockroom"
},
{
"Name": "retirement_date"
},
{
"Name": "sys_domain"
},
{
"Name": "owned_by"
},
{
"Name": "model"
},
{
"Name": "delivery_date"
},
{
"Name": "quantity"
}
]
}
}
Human Readable Output
15. Query computers
Queries the cmdb_ci_computer table in ServiceNow.
Base Command
servicenow-query-computers
Input
| Argument Name | Description | Required |
|---|---|---|
| computer_id | Query by computer sys_id | Optional |
| computer_name | Query by computer name | Optional |
| query | Query by specified query, for more information about querying in ServiceNow, see https://docs.servicenow.com/bundle/istanbul-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html | Optional |
| asset_tag | Query by asset tag | Optional |
| limit | Query results limit | Optional |
| offset | Starting record index to begin retrieving records from | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Computer.ID | string | Computer sys_id |
| ServiceNow.Computer.AssetTag | string | Computer Asset tag |
| ServiceNow.Computer.Name | string | Computer name |
| ServiceNow.Computer.DisplayName | string | Computer display name |
| ServiceNow.Computer.SupportGroup | string | Computer support group |
| ServiceNow.Computer.OperatingSystem | string | Computer operating system |
| ServiceNow.Computer.Company | string | Computer company sys_id |
| ServiceNow.Computer.AssignedTo | string | Computer assigned to user sys_id |
| ServiceNow.Computer.State | string | Computer state |
| ServiceNow.Computer.Cost | string | Computer cost |
| ServiceNow.Computer.Comments | string | Computer comments |
Command Example
!servicenow-query-computers
Context Example
{
"ServiceNow": {
"Computer": [
{
"AssetTag": "P1000503",
"AssignedTo": "92826bf03710200044e0bfc8bcbe5dbb",
"Company": "81fbfe03ac1d55eb286d832de58ae1fd",
"Cost": "1799.99 USD",
"DisplayName": "P1000503 - MacBook Pro 15\"",
"ID": "00a96c0d3790200044e0bfc8bcbe5db4",
"Name": "MacBook Pro 15\"",
"OperatingSystem": "Mac OS 10 (OS/X)",
"State": "In use"
},
{
"AssetTag": "P1000640",
"AssignedTo": "3682abf03710200044e0bfc8bcbe5d09",
"Company": "81fca4cbac1d55eb355b4b6db0e3c80f",
"Cost": "1599.99 USD",
"DisplayName": "P1000640 - MacBook Air 13\"",
"ID": "00a9a80d3790200044e0bfc8bcbe5d1c",
"Name": "MacBook Air 13\"",
"OperatingSystem": "Mac OS 10 (OS/X)",
"State": "In use"
},
{
"AssetTag": "P1000815",
"AssignedTo": "86826bf03710200044e0bfc8bcbe5d42",
"Company": "81fd65ecac1d55eb42a426568fc87a63",
"Cost": "2499.99 USD",
"DisplayName": "P1000815 - MacBook Pro 17\"",
"ID": "00a9e80d3790200044e0bfc8bcbe5d42",
"Name": "MacBook Pro 17\"",
"OperatingSystem": "Mac OS 10 (OS/X)",
"State": "In use"
},
{
"AssetTag": "P1000814",
"AssignedTo": "4e826bf03710200044e0bfc8bcbe5d41",
"Company": "81fd65ecac1d55eb42a426568fc87a63",
"Cost": "2499.99 USD",
"DisplayName": "P1000814 - MacBook Pro 17\"",
"ID": "00a9e80d3790200044e0bfc8bcbe5de8",
"Name": "MacBook Pro 17\"",
"OperatingSystem": "Mac OS 10 (OS/X)",
"State": "In use"
},
{
"AssetTag": "P1000517",
"AssignedTo": "12826bf03710200044e0bfc8bcbe5dc3",
"Company": "81fca4cbac1d55eb355b4b6db0e3c80f",
"Cost": "1799.99 USD",
"DisplayName": "P1000517 - MacBook Pro 15\"",
"ID": "00a9ec0d3790200044e0bfc8bcbe5d06",
"Name": "MacBook Pro 15\"",
"OperatingSystem": "Mac OS 10 (OS/X)",
"State": "In use"
},
{
"AssetTag": "P1000666",
"AssignedTo": "fe82abf03710200044e0bfc8bcbe5d18",
"Company": "a66b1fb03710200044e0bfc8bcbe5d08",
"Cost": "1599.99 USD",
"DisplayName": "P1000666 - MacBook Air 13\"",
"ID": "01a9280d3790200044e0bfc8bcbe5d79",
"Name": "MacBook Air 13\"",
"OperatingSystem": "Mac OS 10 (OS/X)",
"State": "In use"
},
{
"AssetTag": "P1000871",
"AssignedTo": "4e826bf03710200044e0bfc8bcbe5d6f",
"Company": "31bea3d53790200044e0bfc8bcbe5dec",
"Cost": "2499.99 USD",
"DisplayName": "P1000871 - MacBook Pro 17\"",
"ID": "01a92c0d3790200044e0bfc8bcbe5d27",
"Name": "MacBook Pro 17\"",
"OperatingSystem": "Mac OS 10 (OS/X)",
"State": "In use"
},
{
"AssetTag": "P1000429",
"AssignedTo": "c6826bf03710200044e0bfc8bcbe5d90",
"Company": "4b7d13f03710200044e0bfc8bcbe5db6",
"Cost": "2249.99 USD",
"DisplayName": "P1000429 - ThinkStation C20",
"ID": "01a9e40d3790200044e0bfc8bcbe5dab",
"Name": "ThinkStation C20",
"OperatingSystem": "Windows XP Professional",
"State": "In use"
},
{
"AssetTag": "P1000440",
"AssignedTo": "8a826bf03710200044e0bfc8bcbe5d96",
"Company": "81fca4cbac1d55eb355b4b6db0e3c80f",
"Cost": "1699.99 USD",
"DisplayName": "P1000440 - ThinkStation S20",
"ID": "01a9ec0d3790200044e0bfc8bcbe5dc3",
"Name": "ThinkStation S20",
"OperatingSystem": "Windows XP Professional",
"State": "In use"
},
{
"AssetTag": "P1000357",
"AssignedTo": "ce826bf03710200044e0bfc8bcbe5d67",
"Company": "a66b1fb03710200044e0bfc8bcbe5d08",
"Cost": "1329 USD",
"DisplayName": "P1000357 - Precision T5500 Workstation",
"ID": "02a9604d3790200044e0bfc8bcbe5d17",
"Name": "Precision T5500 Workstation",
"OperatingSystem": "Windows XP Professional",
"State": "In use"
}
]
}
}
Human Readable Output
16. Query groups
Queries the sys_user_group table in ServiceNow.
Base Command
servicenow-query-groups
Input
| Argument Name | Description | Required |
|---|---|---|
| group_id | Query by group sys_id | Optional |
| group_name | Query by group name | Optional |
| query | Query by specified query, for more information about querying in ServiceNow, see the ServiceNow documentation. | Optional |
| limit | Query results limit | Optional |
| offset | Starting record index to begin retrieving records from | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Group.ID | string | Group sys_id |
| ServiceNow.Group.Description | string | Group description |
| ServiceNow.Group.Name | string | Group name |
| ServiceNow.Group.Manager | string | Group manager sys_id |
| ServiceNow.Group.Updated | date | Group update time |
Command Example
!servicenow-query-groups group_name=test1
Context Example
{
"ServiceNow": {
"Group": {
"Active": "true",
"Description": "Incident Management Group",
"ID": "12a586cd0bb23200ecfd818393673a30",
"Name": "test1",
"Updated": "2018-11-26 14:51:41"
}
}
}
Human Readable Output
17. Query users
Queries the sys_user table in ServiceNow.
Base Command
servicenow-query-users
Input
| Argument Name | Description | Required |
|---|---|---|
| user_id | Query by user sys_id | Optional |
| user_name | Query by username | Optional |
| query | Query by specified query, for more information about querying in ServiceNow, see the ServiceNow documentation. | Optional |
| limit | Query results limit | Optional |
| offset | Starting record index to begin retrieving records from | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.User.ID | string | User sys_id |
| ServiceNow.User.Name | string | User name (first, middle, last) |
| ServiceNow.User.UserName | string | User username |
| ServiceNow.User.Email | string | User email |
| ServiceNow.User.Created | date | User creation time |
| ServiceNow.User.Updated | date | User update time |
Command Example
!servicenow-query-users user_name=sean.bonnet
Context Example
{
"ServiceNow": {
"User": {
"Created": "2012-02-18 03:04:50",
"Email": "sean.bonnet@example.com",
"ID": "02826bf03710200044e0bfc8bcbe5d6d",
"Name": "Sean Testman",
"Updated": "2018-08-12 22:18:39",
"UserName": "sean.bonnet"
}
}
}
Human Readable Output
18. Get table names
Retrieves table names by a label to use in commands.
Base Command
servicenow-get-table-name
Input
| Argument Name | Description | Required |
|---|---|---|
| label | The table label, e.g., Asset, Incident, IP address, etc. | Required |
| limit | The number of results to return | Optional |
| offset | Starting record index to begin retrieving records from | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Table.ID | string | Table system ID |
| ServiceNow.Table.Name | string | Table name to use in commands, e.g., alm_asset |
| ServiceNow.Table.SystemName | string | Table system name, e.g., Asset |
Command Example
!servicenow-get-table-name label=Asset
Context Example
{
"ServiceNow": {
"Table": {
"ID": "56cc519d5d521300651727c3e282e819",
"Name": "alm_asset",
"SystemName": "Asset"
}
}
}
Human Readable Output
19. Get ticket notes
Returns notes for a specified ticket.
Base Command
servicenow-get-ticket-notes
Input
| Argument Name | Description | Required |
|---|---|---|
| id | Ticket System ID | Required |
| limit | Limit for the ticket notes | Optional |
| offset | Offset of the ticket notes | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| ServiceNow.Ticket.ID | string | Ticket ID |
| ServiceNow.Ticket.Note.Value | unknown | Ticket note value |
| ServiceNow.Ticket.Note.CreatedOn | date | Ticket note created on date |
| ServiceNow.Ticket.Note.CreatedBy | string | Ticket note created by |
| ServiceNow.Ticket.Note.Type | string | Ticket note type |
Command Example
!servicenow-get-ticket-notes id=9c573169c611228700193229fff72400
Context Example
{
"ServiceNow": {
"Ticket": {
"ID": "9c573169c611228700193229fff72400",
"Note": [
{
"CreatedBy": "admin",
"CreatedOn": "2019-01-31 08:40:56",
"Type": "Comment",
"Value": "test"
},
{
"CreatedBy": "admin",
"CreatedOn": "2019-01-31 11:09:14",
"Type": "Work Note",
"Value": "hmm"
}
]
}
}
Human Readable Output
Additional Information
The tables and fields in the ServiceNow UI are different than those in the API. Each table and field name in the UI have their representation in the API.
The integration allows viewing the API table name with the servicenow-get-table command and the API fields(columns) for each table using the servicenow-list-fields command, given the table has records.
In the API, fields that are linked to records in a table (e.g., user) return as an object of link and value as the link refers to the link to that record in the API and the value refers to the system ID of the record. By default, the integration retrieves the value of the object - which means its system ID.
Troubleshooting
If there is an error within ServiceNow, the message is usually indicative, e.g., User Not Authenticated, details: Required to provide Auth information , Invalid table or No Record found, details: Record doesn't exist or ACL restricts the record retrieval.
However there may be connection issues, e.g., when the ServiceNow server URL is incorrect or is unreachable.
Configuration parameters
url— ServiceNow URL, in the format https://company.service-now.com/ (required)credentials— Usernameproxy— Use system proxy settingsinsecure— Trust any certificate (not secure)ticket_type— Default ticket type for running ticket commands and fetching incidentsapi_version— ServiceNow API Version (e.g. 'v1')isFetch— Fetch incidentssysparm_query— The query to use when fetching incidentsfetch_limit— How many incidents to fetch each timefetch_time— First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)timestamp_field— Timestamp field to filter by (e.g., `opened_at`) This is how the filter is applied to the query: “ORDERBYopened_at^opened_at>[Last Run]. To prevent duplicate incidents, this field is mandatory for fetching incidents.incidentType— Incident typeget_attachments— Get incident attachments
Commands (31)
-
servicenow-add-commentDeprecatedAdd comment to specific ticket by providing ticket id
-
servicenow-add-linkDeprecatedAdd a link to specific ticket
-
servicenow-createDeprecatedDeprecated. Use servicenow-create-ticket or servicenow-create-record instead
-
servicenow-create-recordDeprecatedCreate a new record in a specified ServiceNow table
-
servicenow-create-ticketDeprecatedCreate new ServiceNow ticket
-
servicenow-delete-recordDeprecatedDelete a record in a specified ServiceNow table
-
servicenow-delete-ticketDeprecatedDelete a ticket from ServiceNow
-
servicenow-getDeprecatedDeprecated. Use servicenow-get-ticket or servicenow-get-record instead
-
servicenow-get-computerDeprecatedDeprecated. Use servicenow-query-computers instead.
-
servicenow-get-groupsDeprecatedDeprecated. Use servicenow-query-groups instead.
-
servicenow-get-recordDeprecatedRetrieve record information by specific record ID
-
servicenow-get-table-nameDeprecatedGet table names by a label to use in commands
-
servicenow-get-ticketDeprecatedRetrieve ticket information by specific ticket ID
-
servicenow-get-ticket-notesDeprecatedGet notes from the specified ServiceNow ticket - Read permissions are required for the sys_journal_field table.
-
servicenow-incident-add-commentDeprecatedDeprecated. Use servicenow-add-comment instead.
-
servicenow-incident-add-linkDeprecatedDeprecated. Use servicenow-add-link instead.
-
servicenow-incident-createDeprecatedCreate a new ServiceNow ticket
-
servicenow-incident-updateDeprecatedDeprecated. Use servicenow-update-ticket or servicenow-update-record instead
-
servicenow-incident-upload-fileDeprecatedDeprecated. Use servicenow-upload-file instead.
-
servicenow-incidents-queryDeprecatedDeprecated. Use servicenow-query-table or servicenow-query-tickets instead.
-
servicenow-list-table-fieldsDeprecatedList API fields for a specified ServiceNow table
-
servicenow-queryDeprecatedDeprecated. Use servicenow-query-table or servicenow-query-tickets instead.
-
servicenow-query-computersDeprecatedQuery the cmdb_ci_computer table in ServiceNow
-
servicenow-query-groupsDeprecatedQuery the sys_user_group table in ServiceNow
-
servicenow-query-tableDeprecatedQuery a specified table in ServiceNow
-
servicenow-query-ticketsDeprecatedRetrieve ticket info with a query
-
servicenow-query-usersDeprecatedQuery the sys_user table in ServiceNow
-
servicenow-updateDeprecatedDeprecated. Use servicenow-update-ticket or servicenow-update-record instead
-
servicenow-update-recordDeprecatedUpdate a record in a specified ServiceNow table
-
servicenow-update-ticketDeprecatedUpdate specific ticket
-
servicenow-upload-fileDeprecatedUpload a file to a specific ticket
import demistomock as demisto from CommonServerPython import * from CommonServerUserPython import * import re import requests import json from datetime import datetime import shutil # disable insecure warnings requests.packages.urllib3.disable_warnings() if not demisto.params().get('proxy', False): del os.environ['HTTP_PROXY'] del os.environ['HTTPS_PROXY'] del os.environ['http_proxy'] del os.environ['https_proxy'] def get_server_url(): url = demisto.params()['url'] url = re.sub('/[\/]+$/', '', url) url = re.sub('\/$', '', url) return url ''' GLOBAL VARIABLES ''' DEFAULTS = { 'limit': 10, 'offset': 0, 'fetch_limit': 10, 'fetch_time': '10 minutes', 'ticket_type': 'incident' } USERNAME = demisto.params()['credentials']['identifier'] PASSWORD = demisto.params()['credentials']['password'] VERIFY_SSL = not demisto.params().get('insecure', False) API = '/api/now/' VERSION = demisto.params().get('api_version') PARAMS_TICKET_TYPE = demisto.params().get('ticket_type', DEFAULTS['ticket_type']) FETCH_TIME = demisto.params().get('fetch_time').strip() SYSPARM_QUERY = demisto.params().get('sysparm_query') SYSPARM_LIMIT = demisto.params().get('fetch_limit', DEFAULTS['fetch_limit']) TIMESTAMP_FIELD = demisto.params().get('timestamp_field', 'opened_at') TICKET_TYPE = demisto.params().get('ticket_type', DEFAULTS['ticket_type']) GET_ATTACHMENTS = demisto.params().get('get_attachments', False) if VERSION: API += VERSION + '/' SERVER_URL = get_server_url() + API TICKET_STATES = { 'incident': { '1': '1 - New', '2': '2 - In Progress', '3': '3 - On Hold', '4': '4 - Awaiting Caller', '5': '5 - Awaiting Evidence', '6': '6 - Resolved', '7': '7 - Closed', '8': '8 - Canceled' }, 'problem': { '1': '1 - Open', '2': '2 - Known Error', '3': '3 - Pending Change', '4': '4 - Closed/Resolved' }, 'change_request': { '-5': '-5 - New', '-4': '-4 - Assess', '-3': '-3 - Authorize', '-2': '-2 - Scheduled', '-1': '-1 - Implement', '0': '0 - Review', '3': '3 - Closed', '4': '4 - Canceled' }, 'sc_task': { '-5': '-5 - Pending', '1': '1 - Open', '2': '2 - Work In Progress', '3': '3 - Closed Complete', '4': '4 - Closed Incomplete', '7': '7 - Closed Skipped' }, 'sc_request': { '1': '1 - Approved', '3': '3 - Closed', '4': '4 - Rejected' } } TICKET_SEVERITY = { '1': '1 - High', '2': '2 - Medium', '3': '3 - Low' } TICKET_PRIORITY = { '1': '1 - Critical', '2': '2 - High', '3': '3 - Moderate', '4': '4 - Low', '5': '5 - Planning' } COMPUTER_STATUS = { '1': 'In use', '2': 'On order', '3': 'On maintenance', '6': 'In stock/In transit', '7': 'Retired', '100': 'Missing' } # Map SNOW severity to Demisto severity for incident creation SEVERITY_MAP = { '1': 3, '2': 2, '3': 1 } SNOW_ARGS = ['active', 'activity_due', 'opened_at', 'short_description', 'additional_assignee_list', 'approval_history', 'approval_set', 'assigned_to', 'assignment_group', 'business_duration', 'business_service', 'business_stc', 'calendar_duration', 'calendar_stc', 'caller_id', 'caused_by', 'close_code', 'close_notes', 'closed_at', 'closed_by', 'cmdb_ci', 'comments', 'comments_and_work_notes', 'company', 'contact_type', 'correlation_display', 'correlation_id', 'delivery_plan', 'delivery_task', 'description', 'due_date', 'expected_start', 'follow_up', 'group_list', 'hold_reason', 'impact', 'incident_state', 'knowledge', 'location', 'made_sla', 'notify', 'order', 'parent', 'parent_incident', 'priority', 'problem_id', 'resolved_at', 'resolved_by', 'rfc', 'severity', 'sla_due', 'state', 'subcategory', 'sys_tags', 'time_worked', 'urgency', 'user_input', 'watch_list', 'work_end', 'work_notes', 'work_notes_list', 'work_start', 'impact', 'incident_state', 'title', 'type', 'change_type', 'category', 'state', 'caller'] # Every table in ServiceNow should have those fields DEFAULT_RECORD_FIELDS = { 'sys_id': 'ID', 'sys_updated_by': 'UpdatedBy', 'sys_updated_on': 'UpdatedAt', 'sys_created_by': 'CreatedBy', 'sys_created_on': 'CreatedAt' } DEPRECATED_COMMANDS = ['servicenow-get', 'servicenow-incident-get', 'servicenow-create', 'servicenow-incident-create', 'servicenow-update', 'servicenow-query', 'servicenow-incidents-query', 'servicenow-incident-update'] ''' HELPER FUNCTIONS ''' def send_request(path, method='get', body=None, params=None, headers=None, file=None): body = body if body is not None else {} params = params if params is not None else {} url = '{}{}'.format(SERVER_URL, path) if not headers: headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' } if file: # Not supported in v2 url = url.replace('v2', 'v1') try: file_entry = file['id'] file_name = file['name'] shutil.copy(demisto.getFilePath(file_entry)['path'], file_name) with open(file_name, 'rb') as f: files = {'file': f} res = requests.request(method, url, headers=headers, params=params, data=body, files=files, auth=(USERNAME, PASSWORD), verify=VERIFY_SSL) shutil.rmtree(demisto.getFilePath(file_entry)['name'], ignore_errors=True) except Exception as e: raise Exception('Failed to upload file - ' + str(e)) else: res = requests.request(method, url, headers=headers, data=json.dumps(body) if body else {}, params=params, auth=(USERNAME, PASSWORD), verify=VERIFY_SSL) try: obj = res.json() except Exception as e: if not res.content: return '' raise Exception('Error parsing reply - {} - {}'.format(res.content, str(e))) if 'error' in obj: message = obj.get('error', {}).get('message') details = obj.get('error', {}).get('detail') if message == 'No Record found': return { # Return an empty results array 'result': [] } raise Exception('ServiceNow Error: {}, details: {}'.format(message, details)) if res.status_code < 200 or res.status_code >= 300: raise Exception('Got status code {} with url {} with body {} with headers {}' .format(str(res.status_code), url, str(res.content), str(res.headers))) return obj def get_table_name(ticket_type=None): if ticket_type: return ticket_type else: if PARAMS_TICKET_TYPE: return PARAMS_TICKET_TYPE else: return 'incident' def create_ticket_context(data, ticket_type): context = { 'ID': data.get('sys_id'), 'Summary': data.get('short_description'), 'Number': data.get('number'), 'CreatedOn': data.get('sys_created_on'), 'Active': data.get('active'), 'AdditionalComments': data.get('comments'), 'CloseCode': data.get('close_code'), 'OpenedAt': data.get('opened_at') } # These fields refer to records in the database, the value is their system ID. if 'closed_by' in data: context['ResolvedBy'] = data['closed_by']['value'] if 'value' in data['closed_by'] else '' if 'opened_by' in data: context['OpenedBy'] = data['opened_by']['value'] if 'value' in data['opened_by'] else '' context['Creator'] = data['opened_by']['value'] if 'value' in data['opened_by'] else '' if 'assigned_to' in data: context['Assignee'] = data['assigned_to']['value'] if 'value' in data['assigned_to'] else '' # Try to map fields if 'priority' in data: # Backward compatibility if demisto.command() in DEPRECATED_COMMANDS: context['Priority'] = data['priority'] else: context['Priority'] = TICKET_PRIORITY.get(data['priority'], data['priority']) if 'state' in data: mapped_state = data['state'] # Backward compatibility if demisto.command() not in DEPRECATED_COMMANDS: if ticket_type in TICKET_STATES: mapped_state = TICKET_STATES[ticket_type].get(data['state'], mapped_state) context['State'] = mapped_state return createContext(context, removeNull=True) def get_ticket_context(data, ticket_type): if not isinstance(data, list): return create_ticket_context(data, ticket_type) tickets = [] for d in data: tickets.append(create_ticket_context(d, ticket_type)) return tickets def get_ticket_human_readable(tickets, ticket_type): if not isinstance(tickets, list): tickets = [tickets] result = [] for ticket in tickets: hr = { 'Number': ticket.get('number'), 'System ID': ticket['sys_id'], 'Created On': ticket.get('sys_created_on'), 'Created By': ticket.get('sys_created_by'), 'Active': ticket.get('active'), 'Close Notes': ticket.get('close_notes'), 'Close Code': ticket.get('close_code'), 'Description': ticket.get('description'), 'Opened At': ticket.get('opened_at'), 'Due Date': ticket.get('due_date'), # This field refers to a record in the database, the value is its system ID. 'Resolved By': ticket.get('closed_by', {}).get('value') if isinstance(ticket.get('closed_by'), dict) else ticket.get('closed_by'), 'Resolved At': ticket.get('resolved_at'), 'SLA Due': ticket.get('sla_due'), 'Short Description': ticket.get('short_description'), 'Additional Comments': ticket.get('comments') } # Try to map the fields if 'impact' in ticket: hr['Impact'] = TICKET_SEVERITY.get(ticket['impact'], ticket['impact']) if 'urgency' in ticket: hr['Urgency'] = TICKET_SEVERITY.get(ticket['urgency'], ticket['urgency']) if 'severity' in ticket: hr['Severity'] = TICKET_SEVERITY.get(ticket['severity'], ticket['severity']) if 'priority' in ticket: hr['Priority'] = TICKET_PRIORITY.get(ticket['priority'], ticket['priority']) if 'state' in ticket: mapped_state = ticket['state'] if ticket_type in TICKET_STATES: mapped_state = TICKET_STATES[ticket_type].get(ticket['state'], mapped_state) hr['State'] = mapped_state result.append(hr) return result def get_ticket_fields(template, ticket_type): # Inverse the keys and values of those dictionaries to map the arguments to their corresponding values in ServiceNow args = unicode_to_str_recur(demisto.args()) inv_severity = {v: k for k, v in TICKET_SEVERITY.iteritems()} inv_priority = {v: k for k, v in TICKET_PRIORITY.iteritems()} states = TICKET_STATES.get(ticket_type) inv_states = {v: k for k, v in states.iteritems()} if states else {} body = {} for arg in SNOW_ARGS: input_arg = args.get(arg) if input_arg: if arg in ['impact', 'urgency', 'severity']: body[arg] = inv_severity.get(input_arg, input_arg) elif arg == 'priority': body[arg] = inv_priority.get(input_arg, input_arg) elif arg == 'state': body[arg] = inv_states.get(input_arg, input_arg) else: body[arg] = input_arg elif template and arg in template: body[arg] = template[arg] return body def get_body(fields, custom_fields): body = {} if fields: for field in fields: body[field] = fields[field] if custom_fields: for field in custom_fields: # custom fields begin with "u_" if field.startswith('u_'): body[field] = custom_fields[field] else: body['u_' + field] = custom_fields[field] return body def split_fields(fields): dic_fields = {} if fields: # As received by the command arr_fields = fields.split(';') for f in arr_fields: field = f.split('=') if len(field) > 1: dic_fields[field[0]] = field[1] return dic_fields # Converts unicode elements of obj (incl. dictionary and list) to string recursively def unicode_to_str_recur(obj): if isinstance(obj, dict): obj = {unicode_to_str_recur(k): unicode_to_str_recur(v) for k, v in obj.iteritems()} elif isinstance(obj, list): obj = map(unicode_to_str_recur, obj) elif isinstance(obj, unicode): obj = obj.encode('utf-8') return obj # Converts to an str def convert_to_str(obj): if isinstance(obj, unicode): return obj.encode('utf-8') try: return str(obj) except ValueError: return obj ''' FUNCTIONS ''' def get_template(name): query_params = {'sysparm_limit': 1, 'sysparm_query': 'name=' + name} ticket_type = 'sys_template' path = 'table/' + ticket_type res = send_request('GET', path, params=query_params) if len(res['result']) == 0: raise ValueError("Incorrect template name") template = res['result'][0]['template'].split('^') dic_template = {} for i in range(len(template) - 1): template_value = template[i].split('=') if len(template_value) > 1: dic_template[template_value[0]] = template_value[1] return dic_template def get_ticket_command(): args = unicode_to_str_recur(demisto.args()) ticket_type = get_table_name(args.get('ticket_type')) ticket_id = args.get('id') number = args.get('number') get_attachments = args.get('get_attachments', 'false') res = get(ticket_type, ticket_id, number) if not res or 'result' not in res: return 'Cannot find ticket' if isinstance(res['result'], list): if len(res['result']) == 0: return 'Cannot find ticket' ticket = res['result'][0] else: ticket = res['result'] entries = [] # type: List[Dict] if get_attachments.lower() != 'false': entries = get_ticket_attachment_entries(ticket['sys_id']) hr = get_ticket_human_readable(ticket, ticket_type) context = get_ticket_context(ticket, ticket_type) headers = ['System ID', 'Number', 'Impact', 'Urgency', 'Severity', 'Priority', 'State', 'Created On', 'Created By', 'Active', 'Close Notes', 'Close Code', 'Description', 'Opened At', 'Due Date', 'Resolved By', 'Resolved At', 'SLA Due', 'Short Description', 'Additional Comments'] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow ticket', hr, headers=headers, removeNull=True), 'EntryContext': { 'Ticket(val.ID===obj.ID)': context, 'ServiceNow.Ticket(val.ID===obj.ID)': context } } entries.append(entry) return entries def get_record_command(): args = unicode_to_str_recur(demisto.args()) table_name = args['table_name'] record_id = args['id'] fields = args.get('fields') res = get(table_name, record_id) if not res or 'result' not in res: return 'Cannot find record' if isinstance(res['result'], list): if len(res['result']) == 0: return 'Cannot find record' record = res['result'][0] else: record = res['result'] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'] } if fields: fields = argToList(fields) if 'sys_id' not in fields: # ID is added by default fields.append('sys_id') # filter the record for the required fields record = dict(filter(lambda kv_pair: kv_pair[0] in fields, record.items())) for k, v in record.iteritems(): if isinstance(v, dict): # For objects that refer to a record in the database, take their value(system ID). record[k] = v.get('value', record[k]) record['ID'] = record.pop('sys_id') entry['ReadableContentsFormat'] = formats['markdown'] entry['HumanReadable'] = tableToMarkdown('ServiceNow record', record, removeNull=True) entry['EntryContext'] = { 'ServiceNow.Record(val.ID===obj.ID)': createContext(record) } else: mapped_record = {DEFAULT_RECORD_FIELDS[k]: record[k] for k in DEFAULT_RECORD_FIELDS if k in record} entry['ReadableContentsFormat'] = formats['markdown'] entry['HumanReadable'] = tableToMarkdown('ServiceNow record' + record_id, mapped_record, removeNull=True) entry['EntryContext'] = { 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_record) } return entry def get(table_name, record_id, number=None): path = None query_params = {} # type: Dict if record_id: path = 'table/' + table_name + '/' + record_id elif number: path = 'table/' + table_name query_params = { 'number': number } else: # Only in cases where the table is of type ticket raise ValueError('servicenow-get-ticket requires either ticket ID (sys_id) or ticket number') return send_request(path, 'get', params=query_params) def get_ticket_attachments(ticket_id): path = 'attachment' query_params = { 'sysparm_query': 'table_sys_id=' + ticket_id } return send_request(path, 'get', params=query_params) def get_ticket_attachment_entries(ticket_id): entries = [] links = [] # type: List[Tuple[str, str]] attachments_res = get_ticket_attachments(ticket_id) if 'result' in attachments_res and len(attachments_res['result']) > 0: attachments = attachments_res['result'] links = [(attachment['download_link'], attachment['file_name']) for attachment in attachments] for link in links: file_res = requests.get(link[0], auth=(USERNAME, PASSWORD), verify=VERIFY_SSL) if file_res is not None: entries.append(fileResult(link[1], file_res.content)) return entries def update_ticket_command(): args = unicode_to_str_recur(demisto.args()) custom_fields = split_fields(args.get('custom_fields')) template = args.get('template') ticket_type = get_table_name(args.get('ticket_type')) ticket_id = args['id'] if template: template = get_template(template) fields = get_ticket_fields(template, ticket_type) res = update(ticket_type, ticket_id, fields, custom_fields) if not res or 'result' not in res: return_error('Unable to retrieve response') hr = get_ticket_human_readable(res['result'], ticket_type) context = get_ticket_context(res['result'], ticket_type) entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow ticket updated successfully\nTicket type: ' + ticket_type, hr, removeNull=True), 'EntryContext': { 'ServiceNow.Ticket(val.ID===obj.ID)': context } } return entry def update_record_command(): args = unicode_to_str_recur(demisto.args()) table_name = args['table_name'] record_id = args['id'] fields = args.get('fields', {}) custom_fields = args.get('custom_fields') if fields: fields = split_fields(fields) if custom_fields: custom_fields = split_fields(custom_fields) res = update(table_name, record_id, fields, custom_fields) if not res or 'result' not in res: return 'Could not retrieve record' result = res['result'] mapped_record = {DEFAULT_RECORD_FIELDS[k]: result[k] for k in DEFAULT_RECORD_FIELDS if k in result} entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow record updated successfully', mapped_record, removeNull=True), 'EntryContext': { 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_record) } } return entry def update(table_name, record_id, fields, custom_fields): body = get_body(fields, custom_fields) path = 'table/' + table_name + '/' + record_id return send_request(path, 'patch', body=body) def create_ticket_command(): args = unicode_to_str_recur(demisto.args()) custom_fields = split_fields(args.get('custom_fields')) template = args.get('template') ticket_type = get_table_name(args.get('ticket_type')) if template: template = get_template(template) fields = get_ticket_fields(template, ticket_type) res = create(ticket_type, fields, custom_fields) if not res or 'result' not in res: return_error('Unable to retrieve response') hr = get_ticket_human_readable(res['result'], ticket_type) context = get_ticket_context(res['result'], ticket_type) headers = ['System ID', 'Number', 'Impact', 'Urgency', 'Severity', 'Priority', 'State', 'Created On', 'Created By', 'Active', 'Close Notes', 'Close Code', 'Description', 'Opened At', 'Due Date', 'Resolved By', 'Resolved At', 'SLA Due', 'Short Description', 'Additional Comments'] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow ticket created successfully', hr, headers=headers, removeNull=True), 'EntryContext': { 'Ticket(val.ID===obj.ID)': context, 'ServiceNow.Ticket(val.ID===obj.ID)': context } } return entry def create_record_command(): args = unicode_to_str_recur(demisto.args()) table_name = args['table_name'] fields = args.get('fields') custom_fields = args.get('custom_fields') if fields: fields = split_fields(fields) if custom_fields: custom_fields = split_fields(custom_fields) res = create(table_name, fields, custom_fields) if not res or 'result' not in res: return 'Could not retrieve record' result = res['result'] mapped_record = {DEFAULT_RECORD_FIELDS[k]: result[k] for k in DEFAULT_RECORD_FIELDS if k in result} entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow record created successfully', mapped_record, removeNull=True), 'EntryContext': { 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_record) } } return entry def create(table_name, fields, custom_fields): body = get_body(fields, custom_fields) path = 'table/' + table_name return send_request(path, 'post', body=body) def delete_ticket_command(): args = unicode_to_str_recur(demisto.args()) ticket_id = args['id'] ticket_type = get_table_name(args.get('ticket_type')) res = delete(ticket_type, ticket_id) entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['text'], 'HumanReadable': 'Ticket with ID ' + ticket_id + ' was successfully deleted.' } return entry def delete_record_command(): args = unicode_to_str_recur(demisto.args()) record_id = args['id'] table_name = args.get('table_name') res = delete(table_name, record_id) entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['text'], 'HumanReadable': 'Record with ID ' + record_id + ' was successfully deleted.' } return entry def delete(table_name, record_id): path = 'table/' + table_name + '/' + record_id return send_request(path, 'delete') def add_link_command(): args = unicode_to_str_recur(demisto.args()) ticket_id = args['id'] key = 'comments' if args.get('post-as-comment', 'false').lower() == 'true' else 'work_notes' text = args.get('text', args['link']) link = '[code]<a class="web" target="_blank" href="' + args['link'] + '" >' + text + '</a>[/code]' ticket_type = get_table_name(args.get('ticket_type')) res = add_link(ticket_id, ticket_type, key, link) if not res or 'result' not in res: return_error('Unable to retrieve response') headers = ['System ID', 'Number', 'Impact', 'Urgency', 'Severity', 'Priority', 'State', 'Created On', 'Created By', 'Active', 'Close Notes', 'Close Code', 'Description', 'Opened At', 'Due Date', 'Resolved By', 'Resolved At', 'SLA Due', 'Short Description', 'Additional Comments'] hr = get_ticket_human_readable(res['result'], ticket_type) entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('Link successfully added to ServiceNow ticket', hr, headers=headers, removeNull=True) } return entry def add_link(ticket_id, ticket_type, key, link): body = {} body[key] = link path = 'table/' + ticket_type + '/' + ticket_id return send_request(path, 'patch', body=body) def add_comment_command(): args = unicode_to_str_recur(demisto.args()) ticket_id = args['id'] key = 'comments' if args.get('post-as-comment', 'false').lower() == 'true' else 'work_notes' text = args['comment'] ticket_type = get_table_name(args.get('ticket_type')) res = add_comment(ticket_id, ticket_type, key, text) if not res or 'result' not in res: return_error('Unable to retrieve response') headers = ['System ID', 'Number', 'Impact', 'Urgency', 'Severity', 'Priority', 'State', 'Created On', 'Created By', 'Active', 'Close Notes', 'Close Code', 'Description', 'Opened At', 'Due Date', 'Resolved By', 'Resolved At', 'SLA Due', 'Short Description', 'Additional Comments'] hr = get_ticket_human_readable(res['result'], ticket_type) entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('Comment successfully added to ServiceNow ticket', hr, headers=headers, removeNull=True) } return entry def add_comment(ticket_id, ticket_type, key, text): body = {} body[key] = text path = 'table/' + ticket_type + '/' + ticket_id return send_request(path, 'patch', body=body) def get_ticket_notes_command(): args = unicode_to_str_recur(demisto.args()) ticket_id = args['id'] limit = args.get('limit') offset = args.get('offset') comments_query = 'element_id=' + ticket_id + '^element=comments^ORelement=work_notes' res = query('sys_journal_field', limit, offset, comments_query) if not res or 'result' not in res: return 'No results found' headers = ['Value', 'CreatedOn', 'CreatedBy', 'Type'] mapped_notes = [{ 'Value': n.get('value'), 'CreatedOn': n.get('sys_created_on'), 'CreatedBy': n.get('sys_created_by'), 'Type': 'Work Note' if n.get('element', '') == 'work_notes' else 'Comment' } for n in res['result']] if not mapped_notes: return 'No results found' ticket = { 'ID': ticket_id, 'Note': mapped_notes } entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow notes for ticket ' + ticket_id, mapped_notes, headers=headers, headerTransform=pascalToSpace, removeNull=True), 'EntryContext': { 'ServiceNow.Ticket(val.ID===obj.ID)': createContext(ticket, removeNull=True) } } return entry def query_tickets_command(): args = unicode_to_str_recur(demisto.args()) sysparm_limit = args.get('limit', DEFAULTS['limit']) sysparm_query = args.get('query') sysparm_offset = args.get('offset', DEFAULTS['offset']) if not sysparm_query: # backward compatibility sysparm_query = args.get('sysparm_query') ticket_type = get_table_name(args.get('ticket_type')) res = query(ticket_type, sysparm_limit, sysparm_offset, sysparm_query) if not res or 'result' not in res or len(res['result']) == 0: return 'No results found' hr = get_ticket_human_readable(res['result'], ticket_type) context = get_ticket_context(res['result'], ticket_type) headers = ['System ID', 'Number', 'Impact', 'Urgency', 'Severity', 'Priority', 'State', 'Created On', 'Created By', 'Active', 'Close Notes', 'Close Code', 'Description', 'Opened At', 'Due Date', 'Resolved By', 'Resolved At', 'SLA Due', 'Short Description', 'Additional Comments'] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow tickets', hr, headers=headers, removeNull=True), 'EntryContext': { 'Ticket(val.ID===obj.ID)': context, 'ServiceNow.Ticket(val.ID===obj.ID)': context } } return entry def query_table_command(): args = unicode_to_str_recur(demisto.args()) table_name = args['table_name'] sysparm_limit = args.get('limit', DEFAULTS['limit']) sysparm_query = args.get('query') sysparm_offset = args.get('offset', DEFAULTS['offset']) fields = args.get('fields') res = query(table_name, sysparm_limit, sysparm_offset, sysparm_query) if not res or 'result' not in res or len(res['result']) == 0: return 'No results found' entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'] } result = res['result'] if fields: fields = argToList(fields) if 'sys_id' not in fields: # ID is added by default fields.append('sys_id') # Filter the records according to the given fields records = [dict(filter(lambda kv_pair: kv_pair[0] in fields, r.iteritems())) for r in res['result']] for r in records: r['ID'] = r.pop('sys_id') for k, v in r.iteritems(): if isinstance(v, dict): # For objects that refer to a record in the database, take their value (system ID). r[k] = v.get('value', v) entry['ReadableContentsFormat'] = formats['markdown'] entry['HumanReadable'] = tableToMarkdown('ServiceNow records', records, removeNull=True) entry['EntryContext'] = { 'ServiceNow.Record(val.ID===obj.ID)': createContext(records) } else: mapped_records = [{DEFAULT_RECORD_FIELDS[k]: r[k] for k in DEFAULT_RECORD_FIELDS if k in r} for r in result] entry['ReadableContentsFormat'] = formats['markdown'] entry['HumanReadable'] = tableToMarkdown('ServiceNow records', mapped_records, removeNull=True) entry['EntryContext'] = { 'ServiceNow.Record(val.ID===obj.ID)': createContext(mapped_records) } return entry def query(table_name, sysparm_limit, sysparm_offset, sysparm_query): query_params = {} query_params['sysparm_limit'] = sysparm_limit query_params['sysparm_offset'] = sysparm_offset if sysparm_query: query_params['sysparm_query'] = sysparm_query path = 'table/' + table_name return send_request(path, 'get', params=query_params) def upload_file_command(): args = unicode_to_str_recur(demisto.args()) ticket_type = get_table_name(args.get('ticket_type')) ticket_id = args['id'] file_id = args['file_id'] file_name = args.get('file_name', demisto.dt(demisto.context(), "File(val.EntryID=='" + file_id + "').Name")) # in case of info file if not file_name: file_name = demisto.dt(demisto.context(), "InfoFile(val.EntryID=='" + file_id + "').Name") if not file_name: return_error('Could not find the file') file_name = file_name[0] if isinstance(file_name, list) else file_name res = upload_file(ticket_id, file_id, file_name, ticket_type) if not res or 'result' not in res or not res['result']: return_error('Unable to retrieve response') hr = { 'Filename': res['result'].get('file_name'), 'Download link': res['result'].get('download_link'), 'System ID': res['result'].get('sys_id') } context = { 'ID': ticket_id, 'File': {} } context['File']['Filename'] = res['result'].get('file_name') context['File']['Link'] = res['result'].get('download_link') context['File']['SystemID'] = res['result'].get('sys_id') entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('File uploaded successfully', hr), 'EntryContext': { 'ServiceNow.Ticket(val.ID===obj.ID)': context, 'Ticket(val.ID===obj.ID)': context } } return entry def upload_file(ticket_id, file_id, file_name, ticket_type): headers = { 'Accept': 'application/json' } body = { 'table_name': ticket_type, 'table_sys_id': ticket_id, 'file_name': file_name } path = 'attachment/upload' return send_request(path, 'post', headers=headers, body=body, file={'id': file_id, 'name': file_name}) # Deprecated def get_computer_command(): args = unicode_to_str_recur(demisto.args()) table_name = 'cmdb_ci_computer' computer_name = args['computerName'] res = query(table_name, None, 0, 'u_code=' + computer_name) if not res or 'result' not in res: return 'Cannot find computer' elif isinstance(res['result'], list): if len(res['result']) == 0: return 'Cannot find computer' computer = res['result'][0] else: computer = res['result'] if computer['u_code'] != computer_name: return 'Computer not found' hr = { 'ID': computer['sys_id'], 'u_code (computer name)': computer['u_code'], 'Support group': computer['support_group'], 'Operating System': computer['os'], 'Comments': computer['comments'] } ec = createContext(computer, removeNull=True) if 'support_group' in computer: ec['support_group'] = computer['support_group']['value'] if 'value' in computer['support_group'] else '' entry = { 'Type': entryTypes['note'], 'Contents': computer, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow Computer', hr), 'EntryContext': { 'ServiceNowComputer(val.sys_id==obj.sys_id)': ec, } } return entry def query_computers_command(): args = unicode_to_str_recur(demisto.args()) table_name = 'cmdb_ci_computer' computer_id = args.get('computer_id') computer_name = args.get('computer_name') asset_tag = args.get('asset_tag') computer_query = args.get('query', {}) offset = args.get('offset', DEFAULTS['offset']) limit = args.get('limit', DEFAULTS['limit']) if computer_id: res = get(table_name, computer_id) else: if computer_name: computer_query = 'name=' + computer_name elif asset_tag: computer_query = 'asset_tag=' + asset_tag res = query(table_name, limit, offset, computer_query) if not res or 'result' not in res: return 'No computers found' computers = res['result'] if not isinstance(computers, list): computers = [computers] if len(computers) == 0: return 'No computers found' headers = ['ID', 'AssetTag', 'Name', 'DisplayName', 'SupportGroup', 'OperatingSystem', 'Company', 'AssignedTo', 'State', 'Cost', 'Comments'] mapped_computers = [{ 'ID': computer.get('sys_id'), 'AssetTag': computer.get('asset_tag'), 'Name': computer.get('name'), 'DisplayName': '{} - {}'.format(computer.get('asset_tag', ''), computer.get('name', '')), 'SupportGroup': computer.get('support_group'), 'OperatingSystem': computer.get('os'), 'Company': computer.get('company', {}).get('value') if isinstance(computer.get('company'), dict) else computer.get('company'), 'AssignedTo': computer.get('assigned_to', {}).get('value') if isinstance(computer.get('assigned_to'), dict) else computer.get('assigned_to'), 'State': COMPUTER_STATUS.get(computer.get('install_status', ''), computer.get('install_status')), 'Cost': '{} {}'.format(computer.get('cost', ''), computer.get('cost_cc', '')).rstrip(), 'Comments': computer.get('comments') } for computer in computers] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow Computers', mapped_computers, headers=headers, removeNull=True, headerTransform=pascalToSpace), 'EntryContext': { 'ServiceNow.Computer(val.ID===obj.ID)': createContext(mapped_computers, removeNull=True), } } return entry def query_groups_command(): args = unicode_to_str_recur(demisto.args()) table_name = 'sys_user_group' group_id = args.get('group_id') group_name = args.get('group_name') group_query = args.get('query', {}) offset = args.get('offset', DEFAULTS['offset']) limit = args.get('limit', DEFAULTS['limit']) if group_id: res = get(table_name, group_id) else: if group_name: group_query = 'name=' + group_name res = query(table_name, limit, offset, group_query) if not res or 'result' not in res: return 'No groups found' groups = res['result'] if not isinstance(groups, list): groups = [groups] if len(groups) == 0: return 'No groups found' headers = ['ID', 'Description', 'Name', 'Active', 'Manager', 'Updated'] mapped_groups = [{ 'ID': group.get('sys_id'), 'Description': group.get('description'), 'Name': group.get('name'), 'Active': group.get('active'), 'Manager': group.get('manager', {}).get('value') if isinstance(group.get('manager'), dict) else group.get('manager'), 'Updated': group.get('sys_updated_on'), } for group in groups] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow Groups', mapped_groups, headers=headers, removeNull=True, headerTransform=pascalToSpace), 'EntryContext': { 'ServiceNow.Group(val.ID===obj.ID)': createContext(mapped_groups, removeNull=True), } } return entry def query_users_command(): args = unicode_to_str_recur(demisto.args()) table_name = 'sys_user' user_id = args.get('user_id') user_name = args.get('user_name') user_query = args.get('query', {}) offset = args.get('offset', DEFAULTS['offset']) limit = args.get('limit', DEFAULTS['limit']) if user_id: res = get(table_name, user_id) else: if user_name: user_query = 'user_name=' + user_name res = query(table_name, limit, offset, user_query) if not res or 'result' not in res: return 'No users found' res = unicode_to_str_recur(res) users = res['result'] if not isinstance(users, list): users = [users] if len(users) == 0: return 'No users found' headers = ['ID', 'Name', 'UserName', 'Email', 'Created', 'Updated'] mapped_users = [{ 'ID': user.get('sys_id'), 'Name': '{} {}'.format(user.get('first_name', ''), user.get('last_name', '')).rstrip(), 'UserName': user.get('user_name'), 'Email': user.get('email'), 'Created': user.get('sys_created_on'), 'Updated': user.get('sys_updated_on'), } for user in users] mapped_users = unicode_to_str_recur(mapped_users) entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow Users', mapped_users, headers=headers, removeNull=True, headerTransform=pascalToSpace), 'EntryContext': { 'ServiceNow.User(val.ID===obj.ID)': createContext(mapped_users, removeNull=True), } } return entry # Deprecated def get_groups_command(): args = unicode_to_str_recur(demisto.args()) table_name = 'sys_user_group' group_name = args['name'] res = query(table_name, None, 0, 'name=' + group_name) if not res or 'result' not in res: return 'No groups found' hr_groups = [] context_groups = [] for group in res['result']: if group['name'] == group_name: hr_groups.append({ 'ID': group['sys_id'], 'Name': group['name'], 'Description': group['description'], 'Email': group['email'], 'Active': group['active'], 'Manager': ['manager'] }) context_groups.append({ 'GroupId': group['sys_id'], 'GroupName': group['name'] }) entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow Group', hr_groups), 'EntryContext': { 'ServiceNowGroups(val.GroupId==obj.GroupId)': context_groups, } } return entry def list_table_fields_command(): args = unicode_to_str_recur(demisto.args()) table_name = args['table_name'] res = get_table_fields(table_name) if not res or 'result' not in res: return 'Cannot find table' if len(res['result']) == 0: return 'Table contains no records' fields = [{'Name': k} for k, v in res['result'][0].iteritems()] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow Table fields - ' + table_name, fields), 'EntryContext': { 'ServiceNow.Field': createContext(fields), } } return entry def get_table_fields(table_name): # Get one record path = 'table/' + table_name + '?sysparm_limit=1' res = send_request(path, 'GET') return res def get_table_name_command(): args = unicode_to_str_recur(demisto.args()) label = args['label'] offset = args.get('offset', DEFAULTS['offset']) limit = args.get('limit', DEFAULTS['limit']) table_query = 'label=' + label res = query('sys_db_object', limit, offset, table_query) if not res or 'result' not in res: return 'Cannot find table' tables = res['result'] if len(tables) == 0: return 'Cannot find table' headers = ['ID', 'Name', 'SystemName'] mapped_tables = [{ 'ID': table.get('sys_id'), 'Name': table.get('name'), 'SystemName': table.get('sys_name') } for table in tables] entry = { 'Type': entryTypes['note'], 'Contents': res, 'ContentsFormat': formats['json'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('ServiceNow Tables for label - ' + label, mapped_tables, headers=headers, headerTransform=pascalToSpace), 'EntryContext': { 'ServiceNow.Table(val.ID===obj.ID)': createContext(mapped_tables), } } return entry def fetch_incidents(): query_params = {} incidents = [] if FETCH_TIME: fetch_time = FETCH_TIME else: fetch_time = DEFAULTS['fetch_time'] last_run = demisto.getLastRun() if 'time' not in last_run: snow_time, _ = parse_date_range(fetch_time, '%Y-%m-%d %H:%M:%S') else: snow_time = last_run['time'] query = '' if SYSPARM_QUERY: query += SYSPARM_QUERY + '^' query += 'ORDERBY{0}^{0}>{1}'.format(TIMESTAMP_FIELD, snow_time) if query: query_params['sysparm_query'] = query query_params['sysparm_limit'] = SYSPARM_LIMIT path = 'table/' + TICKET_TYPE res = send_request(path, 'get', params=query_params) count = 0 parsed_snow_time = datetime.strptime(snow_time, '%Y-%m-%d %H:%M:%S') for result in res.get('result', []): labels = [] if TIMESTAMP_FIELD not in result: raise ValueError("The timestamp field [{}]" " does not exist in the ticket".format(TIMESTAMP_FIELD)) if count > SYSPARM_LIMIT: break try: if datetime.strptime(result[TIMESTAMP_FIELD], '%Y-%m-%d %H:%M:%S') < parsed_snow_time: continue except Exception: pass for k, v in result.iteritems(): if isinstance(v, basestring): labels.append({ 'type': k, 'value': v }) else: labels.append({ 'type': k, 'value': json.dumps(v) }) severity = SEVERITY_MAP.get(result.get('severity', ''), 0) file_names = [] if GET_ATTACHMENTS: file_entries = get_ticket_attachment_entries(result['sys_id']) for file_result in file_entries: if file_result['Type'] == entryTypes['error']: raise Exception('Error getting attachment: ' + str(file_result['Contents'])) file_names.append({ 'path': file_result['FileID'], 'name': file_result['File'] }) incidents.append({ 'name': 'ServiceNow Incident ' + result.get('number'), 'labels': labels, 'details': json.dumps(result), 'severity': severity, 'attachment': file_names, 'rawJSON': json.dumps(result) }) count += 1 snow_time = result[TIMESTAMP_FIELD] demisto.incidents(incidents) demisto.setLastRun({'time': snow_time}) def test_module(): # Validate fetch_time parameter is valid (if not, parse_date_range will raise the error message) parse_date_range(FETCH_TIME, '%Y-%m-%d %H:%M:%S') path = 'table/' + TICKET_TYPE + '?sysparm_limit=1' res = send_request(path, 'GET') if 'result' not in res: return_error('ServiceNow error: ' + str(res)) ticket = res['result'] if ticket and demisto.params().get('isFetch'): if isinstance(ticket, list): ticket = ticket[0] if TIMESTAMP_FIELD not in ticket: raise ValueError("The timestamp field [{}]" " does not exist in the ticket".format(TIMESTAMP_FIELD)) LOG('Executing command ' + demisto.command()) raise_exception = False try: if demisto.command() == 'test-module': test_module() demisto.results('ok') elif demisto.command() == 'fetch-incidents': raise_exception = True fetch_incidents() elif demisto.command() == 'servicenow-get' or \ demisto.command() == 'servicenow-incident-update' or demisto.command() == 'servicenow-get-ticket': demisto.results(get_ticket_command()) elif demisto.command() == 'servicenow-update' or \ demisto.command() == 'servicenow-incident-update' or demisto.command() == 'servicenow-update-ticket': demisto.results(update_ticket_command()) elif demisto.command() == 'servicenow-create' or \ demisto.command() == 'servicenow-incident-create' or demisto.command() == 'servicenow-create-ticket': demisto.results(create_ticket_command()) elif demisto.command() == 'servicenow-delete-ticket': demisto.results(delete_ticket_command()) elif demisto.command() == 'servicenow-add-link' or demisto.command() == 'servicenow-incident-add-link': demisto.results(add_link_command()) elif demisto.command() == 'servicenow-add-comment' or demisto.command() == 'servicenow-incident-add-comment': demisto.results(add_comment_command()) elif demisto.command() == 'servicenow-query' or \ demisto.command() == 'servicenow-incidents-query' or demisto.command() == 'servicenow-query-tickets': demisto.results(query_tickets_command()) elif demisto.command() == 'servicenow-upload-file' or demisto.command() == 'servicenow-incident-upload-file': demisto.results(upload_file_command()) elif demisto.command() == 'servicenow-query-table': demisto.results(query_table_command()) elif demisto.command() == 'servicenow-get-computer': demisto.results(get_computer_command()) elif demisto.command() == 'servicenow-query-computers': demisto.results(query_computers_command()) elif demisto.command() == 'servicenow-query-groups': demisto.results(query_groups_command()) elif demisto.command() == 'servicenow-query-users': demisto.results(query_users_command()) elif demisto.command() == 'servicenow-get-groups': demisto.results(get_groups_command()) elif demisto.command() == 'servicenow-get-record': demisto.results(get_record_command()) elif demisto.command() == 'servicenow-update-record': demisto.results(update_record_command()) elif demisto.command() == 'servicenow-create-record': demisto.results(create_record_command()) elif demisto.command() == 'servicenow-delete-record': demisto.results(delete_record_command()) if demisto.command() == 'servicenow-list-table-fields': demisto.results(list_table_fields_command()) if demisto.command() == 'servicenow-get-table-name': demisto.results(get_table_name_command()) if demisto.command() == 'servicenow-get-ticket-notes': demisto.results(get_ticket_notes_command()) except Exception as e: LOG(e) LOG.print_log() if not raise_exception: return_error(str(e)) else: raise


















