ServiceNow CMDB

ServiceNow CMDB is a service‑centric foundation that proactively analyzes service‑impacting changes, identifies issues, and eliminates outages.

IT Services · ServiceNow

Details

IDServiceNow CMDB
ProviderServiceNow
CategoryIT Services
From Version5.0.0
Docker Imagedemisto/auth-utils:1.0.0.10133006
Supported ModulesAttack Surface Management Exposure Management Agentix Cortex Cloud Cloud Runtime Security Cloud Posture Security XSIAM EDR

README

ServiceNow CMDB is a service‑centric foundation that proactively
analyzes service‑impacting changes, identifies issues, and eliminates outages.

Configure ServiceNow_CMDB in Cortex

Instance Configuration

The integration supports three types of authorization:

  1. Basic authorization using username and password.
  2. OAuth 2.0 authorization.
  3. JWT authentication.

OAuth 2.0 Authorization

To use OAuth 2.0 authorization, perform the following steps:

  1. Login to your ServiceNow instance and create an endpoint to access your instance (please see Snow OAuth for more information).
  2. Copy the Client Id and Client Secret (click the lock next to the client secret to reveal it) that were automatically generated when creating the endpoint into the Client ID and Client Secret fields of the instance configuration.
  3. (Recommended) Enter the ServiceNow account Username and Password in the instance configuration. When provided, the integration automatically performs the initial OAuth login on first use. It also automatically renews the refresh token when it expires.
  4. Select the Use OAuth Login checkbox and click Done.
  5. If you did not provide Username and Password in step 3, run the command !servicenow-cmdb-oauth-login from the CLI and fill in the username and password of the ServiceNow instance. This step generates and saves to the integration context a refresh token to the ServiceNow instance and is required only the first time after configuring a new instance. If you provided Username and Password, this step is handled automatically.
  6. (Optional) Test the created instance by running the !servicenow-cmdb-oauth-test command.

Notes:

  1. When running the !servicenow-cmdb-oauth-login command, a refresh token is generated and will be used to produce new access tokens after the current access token has expired.
  2. If the Username and Password fields are configured, the integration will automatically perform the initial login and renew the refresh token when it expires — no manual commands are needed. Otherwise, you will have to run the servicenow-cmdb-oauth-login command for the initial login and again each time the refresh token expires.
  3. The grant type used to get an access token is Client credentials. See the Snow documentation for more information.

JWT Authentication

Prerequisites in order to support JWT
  1. Create a Java Key Store and upload it to the instance by accessing from the upper menu: All > System Definition > Certificates. The private key will be used as an integration parameter.
  2. Configure a JWT signing key by accessing: All→System OAuth→JWT Keys using the keystore from above and keep the Key ID as it will be used as kid integration parameter.
  3. Create a JWT provider with a JWT signing key by accessing: All→System OAuth→JWT providers. Claim Name sub in Standard Claims has to be existing non-admin servicenow user with all necessary roles.
  4. Connect to an OAuth provider and create an OAuth application registry by accessing All→System OAuth→Application Registry:
    1. aud in JWT provider has to be equal to Client ID from OAuth JWT application - update JWT provider If necessary.
    2. The value of kid in JWT Verifier Maps has to be the same as Key Id in JWT signing key.
      The value can be updated if necessary.
  5. Create API Access Policy or add Authentication profile to existing Policy by accessing: All→System Web Services→API Access Policies→Rest API Access Policies

IMPORTANT:

  1. The Standard Authentication Profile of type Oauth should be already present in ServiceNow and has to be added to the Policy.
    API Access Policy should be configured as global in order to cover all available resources and not just now/table
  2. Granting JWT to admin is not allowed.
    You should have a non-admin user with all necessary roles (only non-admin roles) in addition to the existing role snc_platform_rest_api_access that is required to make API calls.

Using Multi Factor Authentication (MFA)

MFA can be used both when using basic authorization and OAuth 2.0 authorization, however we strongly recommend using OAuth 2.0 when using MFA.
If MFA is enabled for your user, perform the following steps:

  1. Open the Google Authenticator application on your mobile device and make note of the number. The number refreshes every 30 seconds.
  2. Enter your username and password, and append the One Time Password (OTP) that you currently see on your mobile device to your password without any extra spaces. For example, if your password is 12345 and the current OTP code is 424 058, enter 12345424058.

Notes:

  1. When using basic authorization, you will have to update your password with the current OTP every time the current code expires (30 seconds), therefore, we recommend using OAuth 2.0 authorization.
  2. For using OAuth 2.0 see the above instructions. The OTP code should be appended to the password parameter in the !servicenow-cmdb-oauth-login command.
Parameter Description Required
ServiceNow URL, in the format https://company.service-now.com/   True
Use OAuth Login Check this box to use OAuth 2.0 authentication. See (?) for more information. False
Use JWT Authentication Select this checkbox to use JWT authentication. See (?) for more information. False
Client ID/JWT Audience (aud)   False
Client Secret   False
Username   False
Password   False
Private key   False
Kid (Key Id)   False
Sub   False
Iss (Issuer) If not set, Client ID will be used. False
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

servicenow-cmdb-records-list


Query records for a CMDB class.

Base Command

servicenow-cmdb-records-list

Input

Argument Name Description Required
class The name of the class to query. Required
query An encoded query string used to filter the results. For more information about querying in ServiceNow, see https://docs.servicenow.com/bundle/paris-servicenow-platform/page/use/common-ui-elements/reference/r_OpAvailableFiltersQueries.html Optional
limit The maximum number of results returned per page (default: 50). Optional
offset The number of records to exclude from the query (default: 0). Optional

Context Output

Path Type Description
ServiceNowCMDB.Class String The name of CMDB table that was queried.
ServiceNowCMDB.Records Unknown A list of all the records that were found in the CMDB table.

Command Example

!servicenow-cmdb-records-list class=cmdb_ci_linux_server limit=3

Context Example

{
    "ServiceNowCMDB": {
        "Class": "cmdb_ci_linux_server",
        "Records": [
            {
                "name": "Test Linux Server 2",
                "sys_id": "0ad329e3db27901026fca015ca9619fb"
            },
            {
                "name": "Update Name Test",
                "sys_id": "18295cefdbd0241026fca015ca9619f7"
            },
            {
                "name": "new design updated name",
                "sys_id": "2a41eb4e1b739810042611b4bd4bcb9d"
            }
        ]
    }
}

Human Readable Output

Found 3 records for class cmdb_ci_linux_server

name sys_id
Test Linux Server 2 0ad329e3db27901026fca015ca9619fb
Update Name Test 18295cefdbd0241026fca015ca9619f7
new design updated name 2a41eb4e1b739810042611b4bd4bcb9d

servicenow-cmdb-record-get-by-id


Query attributes and relationship information for a specific record.

Base Command

servicenow-cmdb-record-get-by-id

Input

Argument Name Description Required
class The name of the class to query. Required
sys_id The ID of the record that should be queried. Required
fields A comma-separated list of the fields to return for the queried record. Optional
relation_limit The maximum number of relations returned (default: 50). Optional
relation_offset The number of records to exclude from the relations query (default: 0). Optional

Context Output

Path Type Description
ServiceNowCMDB.Record.SysID String The sys_id of the record that was queried.
ServiceNowCMDB.Record.Class String The name of the class from which the record was queried.
ServiceNowCMDB.Record.Attributes Unknown The attributes that were returned in the response for the queried record.
ServiceNowCMDB.Record.OutboundRelations Unknown A list of all the outbound relations of the queried record.
ServiceNowCMDB.Record.InboundRelations Unknown A list of all the inbound relations of the queried record.

Command Example

!servicenow-cmdb-record-get-by-id class=cmdb_ci_linux_server sys_id=a8decc3f1b9c2410042611b4bd4bcb7d

Context Example

{
    "ServiceNowCMDB": {
        "Record": {
            "Attributes": {
                "asset": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/alm_asset/60decc3f1b9c2410042611b4bd4bcb7f",
                    "value": "60decc3f1b9c2410042611b4bd4bcb7f"
                },
                "asset_tag": "",
                "assigned": "",
                "assigned_to": "",
                "assignment_group": "",
                "attributes": "",
                "can_print": "false",
                "category": "Hardware",
                "cd_rom": "false",
                "cd_speed": "",
                "change_control": "",
                "chassis_type": "",
                "checked_in": "",
                "checked_out": "",
                "classification": "Production",
                "comments": "",
                "company": "",
                "correlation_id": "",
                "cost": "",
                "cost_cc": "USD",
                "cost_center": "",
                "cpu_core_count": "",
                "cpu_core_thread": "",
                "cpu_count": "",
                "cpu_manufacturer": "",
                "cpu_name": "",
                "cpu_speed": "",
                "cpu_type": "",
                "default_gateway": "",
                "delivery_date": "",
                "department": "",
                "discovery_source": "ServiceNow",
                "disk_space": "",
                "dns_domain": "",
                "dr_backup": "",
                "due": "",
                "due_in": "",
                "duplicate_of": "",
                "fault_count": "0",
                "firewall_status": "Intranet",
                "first_discovered": "2020-11-12 06:18:25",
                "floppy": "",
                "form_factor": "",
                "fqdn": "",
                "gl_account": "",
                "hardware_status": "installed",
                "hardware_substatus": "",
                "host_name": "",
                "install_date": "",
                "install_status": "1",
                "invoice_number": "",
                "ip_address": "",
                "justification": "",
                "kernel_release": "",
                "last_discovered": "2020-11-12 06:18:25",
                "lease_id": "",
                "location": "",
                "mac_address": "",
                "maintenance_schedule": "",
                "managed_by": "",
                "manufacturer": "",
                "model_id": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/cmdb_model/4ed329e3db27901026fca015ca9619fc",
                    "value": "4ed329e3db27901026fca015ca9619fc"
                },
                "model_number": "",
                "monitor": "false",
                "name": "Record For README",
                "object_id": "",
                "operational_status": "1",
                "order_date": "",
                "os": "",
                "os_address_width": "",
                "os_domain": "",
                "os_service_pack": "",
                "os_version": "",
                "owned_by": "",
                "po_number": "",
                "purchase_date": "",
                "ram": "",
                "schedule": "",
                "serial_number": "",
                "short_description": "",
                "skip_sync": "false",
                "start_date": "",
                "subcategory": "Computer",
                "support_group": "",
                "supported_by": "",
                "sys_class_name": "cmdb_ci_linux_server",
                "sys_class_path": "/!!/!2/!(/!!/!0",
                "sys_created_by": "admin",
                "sys_created_on": "2020-11-12 06:18:25",
                "sys_domain": {
                    "display_value": "global",
                    "link": "https://ven03941.service-now.com/api/now/table/sys_user_group/global",
                    "value": "global"
                },
                "sys_domain_path": "/",
                "sys_id": "a8decc3f1b9c2410042611b4bd4bcb7d",
                "sys_mod_count": "0",
                "sys_tags": "",
                "sys_updated_by": "admin",
                "sys_updated_on": "2020-11-12 06:18:25",
                "unverified": "false",
                "used_for": "Production",
                "vendor": "",
                "virtual": "false",
                "warranty_expiration": ""
            },
            "Class": "cmdb_ci_linux_server",
            "InboundRelations": [
                {
                    "sys_id": "eb3f84331b5c2410042611b4bd4bcbf9",
                    "target": {
                        "display_value": "CMS App FLX",
                        "link": "https://ven03941.service-now.com/api/now/cmdb/instance/cmdb_ci/829e953a0ad3370200af63483498b1ea",
                        "value": "829e953a0ad3370200af63483498b1ea"
                    },
                    "type": {
                        "display_value": "Uses::Used by",
                        "link": "https://ven03941.service-now.com/api/now/table/cmdb_rel_type/cb5592603751200032ff8c00dfbe5d17",
                        "value": "cb5592603751200032ff8c00dfbe5d17"
                    }
                }
            ],
            "OutboundRelations": [],
            "SysID": "a8decc3f1b9c2410042611b4bd4bcb7d"
        }
    }
}

Human Readable Output

Found the following attributes and relations for record a8decc3f1b9c2410042611b4bd4bcb7d

Attributes

Name SysID
Record For README a8decc3f1b9c2410042611b4bd4bcb7d

Inbound Relations

SysID Target Display Value Type Display Value
eb3f84331b5c2410042611b4bd4bcbf9 CMS App FLX Uses::Used by

servicenow-cmdb-record-create


Create a record with associated relations and attributes.

Base Command

servicenow-cmdb-record-create

Input

Argument Name Description Required
class The name of the class to add the record to. Required
source The discovery source value. You can see all available values from sys_choice table with query GOTOelementLIKEdiscovery_source^name=cmdb_ci. (default: “ServiceNow”). Optional
attributes A comma-separated list of attributes that should be added to the created record. Input format: attribute=value pairs, e.g., “name=test, ram=1024”. Required
inbound_relations A comma-separated list of dictionaries. Each dictionary represents an inbound relation that should be added to the created record. Optional
outbound_relations A comma-separated list of dictionaries. Each dictionary represents an outbound relation that should be added to the created record. Optional
fields A comma-separated list of fields to return for the created record. Optional
relation_limit The maximum number of relations returned (default: 50). Optional
relation_offset The number of records to exclude from the relations query (default: 0). Optional

Context Output

Path Type Description
ServiceNowCMDB.Record.SysID String The sys_id of the record that was created.
ServiceNowCMDB.Record.Class String The name of the class from which the record was created.
ServiceNowCMDB.Record.Attributes Unknown The attributes that were returned in the response for the created record.
ServiceNowCMDB.Record.OutboundRelations Unknown A list of all the outbound relations of the created record.
ServiceNowCMDB.Record.InboundRelations Unknown A list of all the inbound relations of the created record.

Command Example

!servicenow-cmdb-record-create class=cmdb_ci_linux_server attributes="name=README Record"

Context Example

{
    "ServiceNowCMDB": {
        "Record": {
            "Attributes": {
                "asset": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/alm_asset/964098b3db50641026fca015ca9619a8",
                    "value": "964098b3db50641026fca015ca9619a8"
                },
                "asset_tag": "",
                "assigned": "",
                "assigned_to": "",
                "assignment_group": "",
                "attributes": "",
                "can_print": "false",
                "category": "Hardware",
                "cd_rom": "false",
                "cd_speed": "",
                "change_control": "",
                "chassis_type": "",
                "checked_in": "",
                "checked_out": "",
                "classification": "Production",
                "comments": "",
                "company": "",
                "correlation_id": "",
                "cost": "",
                "cost_cc": "USD",
                "cost_center": "",
                "cpu_core_count": "",
                "cpu_core_thread": "",
                "cpu_count": "",
                "cpu_manufacturer": "",
                "cpu_name": "",
                "cpu_speed": "",
                "cpu_type": "",
                "default_gateway": "",
                "delivery_date": "",
                "department": "",
                "discovery_source": "ServiceNow",
                "disk_space": "",
                "dns_domain": "",
                "dr_backup": "",
                "due": "",
                "due_in": "",
                "duplicate_of": "",
                "fault_count": "0",
                "firewall_status": "Intranet",
                "first_discovered": "2020-11-12 06:24:49",
                "floppy": "",
                "form_factor": "",
                "fqdn": "",
                "gl_account": "",
                "hardware_status": "installed",
                "hardware_substatus": "",
                "host_name": "",
                "install_date": "",
                "install_status": "1",
                "invoice_number": "",
                "ip_address": "",
                "justification": "",
                "kernel_release": "",
                "last_discovered": "2020-11-12 06:24:49",
                "lease_id": "",
                "location": "",
                "mac_address": "",
                "maintenance_schedule": "",
                "managed_by": "",
                "manufacturer": "",
                "model_id": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/cmdb_model/4ed329e3db27901026fca015ca9619fc",
                    "value": "4ed329e3db27901026fca015ca9619fc"
                },
                "model_number": "",
                "monitor": "false",
                "name": "README Record",
                "object_id": "",
                "operational_status": "1",
                "order_date": "",
                "os": "",
                "os_address_width": "",
                "os_domain": "",
                "os_service_pack": "",
                "os_version": "",
                "owned_by": "",
                "po_number": "",
                "purchase_date": "",
                "ram": "",
                "schedule": "",
                "serial_number": "",
                "short_description": "",
                "skip_sync": "false",
                "start_date": "",
                "subcategory": "Computer",
                "support_group": "",
                "supported_by": "",
                "sys_class_name": "cmdb_ci_linux_server",
                "sys_class_path": "/!!/!2/!(/!!/!0",
                "sys_created_by": "admin",
                "sys_created_on": "2020-11-12 06:24:49",
                "sys_domain": {
                    "display_value": "global",
                    "link": "https://ven03941.service-now.com/api/now/table/sys_user_group/global",
                    "value": "global"
                },
                "sys_domain_path": "/",
                "sys_id": "d64098b3db50641026fca015ca9619a7",
                "sys_mod_count": "0",
                "sys_tags": "",
                "sys_updated_by": "admin",
                "sys_updated_on": "2020-11-12 06:24:49",
                "unverified": "false",
                "used_for": "Production",
                "vendor": "",
                "virtual": "false",
                "warranty_expiration": ""
            },
            "Class": "cmdb_ci_linux_server",
            "InboundRelations": [],
            "OutboundRelations": [],
            "SysID": "d64098b3db50641026fca015ca9619a7"
        }
    }
}

Human Readable Output

Record d64098b3db50641026fca015ca9619a7 was created successfully

Attributes

Name SysID
README Record d64098b3db50641026fca015ca9619a7

servicenow-cmdb-record-update


Update a record with the given attributes.

Base Command

servicenow-cmdb-record-update

Input

Argument Name Description Required
class The class name of the record that should be updated. Required
sys_id The ID of the record that should be updated. Required
source The discovery source value. You can see all available values from sys_choice table with query GOTOelementLIKEdiscovery_source^name=cmdb_ci. (default: “ServiceNow”). Optional
attributes A comma-separated list of the attributes that should be updated in the record. Input format: attribute=value pairs, e.g., “name=test,ram=1024”. Required
fields A comma-separated list of the fields to return for the updated record. Optional
relation_limit The maximum number of relations returned (default: 50). Optional
relation_offset The number of records to exclude from the relations query (default: 0). Optional

Context Output

Path Type Description
ServiceNowCMDB.Record.SysID String The sys_id of the record that was updated.
ServiceNowCMDB.Record.Class String The class name of the updated record.
ServiceNowCMDB.Record.Attributes Unknown The attributes that were returned in the response for the updated record.
ServiceNowCMDB.Record.OutboundRelations Unknown A list of all the outbound relations of the updated record.
ServiceNowCMDB.Record.InboundRelations Unknown A list of all the inbound relations of the updated record.

Command Example

!servicenow-cmdb-record-update class=cmdb_ci_linux_server sys_id=a8decc3f1b9c2410042611b4bd4bcb7d attributes="name=Update Name README"

Context Example

{
    "ServiceNowCMDB": {
        "Record": {
            "Attributes": {
                "asset": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/alm_asset/60decc3f1b9c2410042611b4bd4bcb7f",
                    "value": "60decc3f1b9c2410042611b4bd4bcb7f"
                },
                "asset_tag": "",
                "assigned": "",
                "assigned_to": "",
                "assignment_group": "",
                "attributes": "",
                "can_print": "false",
                "category": "Hardware",
                "cd_rom": "false",
                "cd_speed": "",
                "change_control": "",
                "chassis_type": "",
                "checked_in": "",
                "checked_out": "",
                "classification": "Production",
                "comments": "",
                "company": "",
                "correlation_id": "",
                "cost": "",
                "cost_cc": "USD",
                "cost_center": "",
                "cpu_core_count": "",
                "cpu_core_thread": "",
                "cpu_count": "",
                "cpu_manufacturer": "",
                "cpu_name": "",
                "cpu_speed": "",
                "cpu_type": "",
                "default_gateway": "",
                "delivery_date": "",
                "department": "",
                "discovery_source": "ServiceNow",
                "disk_space": "",
                "dns_domain": "",
                "dr_backup": "",
                "due": "",
                "due_in": "",
                "duplicate_of": "",
                "fault_count": "0",
                "firewall_status": "Intranet",
                "first_discovered": "2020-11-12 06:18:25",
                "floppy": "",
                "form_factor": "",
                "fqdn": "",
                "gl_account": "",
                "hardware_status": "installed",
                "hardware_substatus": "",
                "host_name": "",
                "install_date": "",
                "install_status": "1",
                "invoice_number": "",
                "ip_address": "",
                "justification": "",
                "kernel_release": "",
                "last_discovered": "2020-11-12 06:24:53",
                "lease_id": "",
                "location": "",
                "mac_address": "",
                "maintenance_schedule": "",
                "managed_by": "",
                "manufacturer": "",
                "model_id": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/cmdb_model/4ed329e3db27901026fca015ca9619fc",
                    "value": "4ed329e3db27901026fca015ca9619fc"
                },
                "model_number": "",
                "monitor": "false",
                "name": "Update Name README",
                "object_id": "",
                "operational_status": "1",
                "order_date": "",
                "os": "",
                "os_address_width": "",
                "os_domain": "",
                "os_service_pack": "",
                "os_version": "",
                "owned_by": "",
                "po_number": "",
                "purchase_date": "",
                "ram": "",
                "schedule": "",
                "serial_number": "",
                "short_description": "",
                "skip_sync": "false",
                "start_date": "",
                "subcategory": "Computer",
                "support_group": "",
                "supported_by": "",
                "sys_class_name": "cmdb_ci_linux_server",
                "sys_class_path": "/!!/!2/!(/!!/!0",
                "sys_created_by": "admin",
                "sys_created_on": "2020-11-12 06:18:25",
                "sys_domain": {
                    "display_value": "global",
                    "link": "https://ven03941.service-now.com/api/now/table/sys_user_group/global",
                    "value": "global"
                },
                "sys_domain_path": "/",
                "sys_id": "a8decc3f1b9c2410042611b4bd4bcb7d",
                "sys_mod_count": "1",
                "sys_tags": "",
                "sys_updated_by": "admin",
                "sys_updated_on": "2020-11-12 06:24:53",
                "unverified": "false",
                "used_for": "Production",
                "vendor": "",
                "virtual": "false",
                "warranty_expiration": ""
            },
            "Class": "cmdb_ci_linux_server",
            "InboundRelations": [
                {
                    "sys_id": "eb3f84331b5c2410042611b4bd4bcbf9",
                    "target": {
                        "display_value": "CMS App FLX",
                        "link": "https://ven03941.service-now.com/api/now/cmdb/instance/cmdb_ci/829e953a0ad3370200af63483498b1ea",
                        "value": "829e953a0ad3370200af63483498b1ea"
                    },
                    "type": {
                        "display_value": "Uses::Used by",
                        "link": "https://ven03941.service-now.com/api/now/table/cmdb_rel_type/cb5592603751200032ff8c00dfbe5d17",
                        "value": "cb5592603751200032ff8c00dfbe5d17"
                    }
                }
            ],
            "OutboundRelations": [],
            "SysID": "a8decc3f1b9c2410042611b4bd4bcb7d"
        }
    }
}

Human Readable Output

Updated record a8decc3f1b9c2410042611b4bd4bcb7d successfully

Attributes

Name SysID
Update Name README a8decc3f1b9c2410042611b4bd4bcb7d

Inbound Relations

SysID Target Display Value Type Display Value
eb3f84331b5c2410042611b4bd4bcbf9 CMS App FLX Uses::Used by

servicenow-cmdb-record-add-relations


Add a new relation to an existing record.

Base Command

servicenow-cmdb-record-add-relations

Input

Argument Name Description Required
class The class name of the record. Required
sys_id The ID of the record to which the relations should be added. Required
source The discovery source value. You can see all available values from sys_choice table with query GOTOelementLIKEdiscovery_source^name=cmdb_ci. (default: “ServiceNow”). Optional
inbound_relations A comma-separated list of dictionaries. Each dictionary represents an inbound relation that should be added to the created record. Optional
outbound_relations A comma-separated list of dictionaries. Each dictionary represents an outbound relation that should be added to the created record. Optional
fields A comma-separated list of the fields to return for the record. Optional
relation_limit The maximum number of relations returned (default: 50). Optional
relation_offset The number of records to exclude from the relations query (default: 0). Optional

Context Output

Path Type Description
ServiceNowCMDB.Record.SysID String The sys_id of the record that was updated.
ServiceNowCMDB.Record.Class String The class name of the record.
ServiceNowCMDB.Record.Attributes Unknown The attributes that were returned in the response for the updated record.
ServiceNowCMDB.Record.OutboundRelations Unknown A list of all the outbound relations of the record.
ServiceNowCMDB.Record.InboundRelations Unknown A list of all the inbound relations of the record.

Command Example

!servicenow-cmdb-record-add-relations class=cmdb_ci_linux_server sys_id=a8decc3f1b9c2410042611b4bd4bcb7d inbound_relations="[{'type': 'cb5592603751200032ff8c00dfbe5d17','target':'829e953a0ad3370200af63483498b1ea','sys_class_name':'cmdb_ci_appl'}]"

Context Example

{
    "ServiceNowCMDB": {
        "Record": {
            "Attributes": {
                "asset": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/alm_asset/60decc3f1b9c2410042611b4bd4bcb7f",
                    "value": "60decc3f1b9c2410042611b4bd4bcb7f"
                },
                "asset_tag": "",
                "assigned": "",
                "assigned_to": "",
                "assignment_group": "",
                "attributes": "",
                "can_print": "false",
                "category": "Hardware",
                "cd_rom": "false",
                "cd_speed": "",
                "change_control": "",
                "chassis_type": "",
                "checked_in": "",
                "checked_out": "",
                "classification": "Production",
                "comments": "",
                "company": "",
                "correlation_id": "",
                "cost": "",
                "cost_cc": "USD",
                "cost_center": "",
                "cpu_core_count": "",
                "cpu_core_thread": "",
                "cpu_count": "",
                "cpu_manufacturer": "",
                "cpu_name": "",
                "cpu_speed": "",
                "cpu_type": "",
                "default_gateway": "",
                "delivery_date": "",
                "department": "",
                "discovery_source": "ServiceNow",
                "disk_space": "",
                "dns_domain": "",
                "dr_backup": "",
                "due": "",
                "due_in": "",
                "duplicate_of": "",
                "fault_count": "0",
                "firewall_status": "Intranet",
                "first_discovered": "2020-11-12 06:18:25",
                "floppy": "",
                "form_factor": "",
                "fqdn": "",
                "gl_account": "",
                "hardware_status": "installed",
                "hardware_substatus": "",
                "host_name": "",
                "install_date": "",
                "install_status": "1",
                "invoice_number": "",
                "ip_address": "",
                "justification": "",
                "kernel_release": "",
                "last_discovered": "2020-11-12 06:24:53",
                "lease_id": "",
                "location": "",
                "mac_address": "",
                "maintenance_schedule": "",
                "managed_by": "",
                "manufacturer": "",
                "model_id": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/cmdb_model/4ed329e3db27901026fca015ca9619fc",
                    "value": "4ed329e3db27901026fca015ca9619fc"
                },
                "model_number": "",
                "monitor": "false",
                "name": "Update Name README",
                "object_id": "",
                "operational_status": "1",
                "order_date": "",
                "os": "",
                "os_address_width": "",
                "os_domain": "",
                "os_service_pack": "",
                "os_version": "",
                "owned_by": "",
                "po_number": "",
                "purchase_date": "",
                "ram": "",
                "schedule": "",
                "serial_number": "",
                "short_description": "",
                "skip_sync": "false",
                "start_date": "",
                "subcategory": "Computer",
                "support_group": "",
                "supported_by": "",
                "sys_class_name": "cmdb_ci_linux_server",
                "sys_class_path": "/!!/!2/!(/!!/!0",
                "sys_created_by": "admin",
                "sys_created_on": "2020-11-12 06:18:25",
                "sys_domain": {
                    "display_value": "global",
                    "link": "https://ven03941.service-now.com/api/now/table/sys_user_group/global",
                    "value": "global"
                },
                "sys_domain_path": "/",
                "sys_id": "a8decc3f1b9c2410042611b4bd4bcb7d",
                "sys_mod_count": "1",
                "sys_tags": "",
                "sys_updated_by": "admin",
                "sys_updated_on": "2020-11-12 06:24:53",
                "unverified": "false",
                "used_for": "Production",
                "vendor": "",
                "virtual": "false",
                "warranty_expiration": ""
            },
            "Class": "cmdb_ci_linux_server",
            "InboundRelations": [
                {
                    "sys_id": "b34050bbdb10641026fca015ca961985",
                    "target": {
                        "display_value": "CMS App FLX",
                        "link": "https://ven03941.service-now.com/api/now/cmdb/instance/cmdb_ci/829e953a0ad3370200af63483498b1ea",
                        "value": "829e953a0ad3370200af63483498b1ea"
                    },
                    "type": {
                        "display_value": "Uses::Used by",
                        "link": "https://ven03941.service-now.com/api/now/table/cmdb_rel_type/cb5592603751200032ff8c00dfbe5d17",
                        "value": "cb5592603751200032ff8c00dfbe5d17"
                    }
                },
                {
                    "sys_id": "eb3f84331b5c2410042611b4bd4bcbf9",
                    "target": {
                        "display_value": "CMS App FLX",
                        "link": "https://ven03941.service-now.com/api/now/cmdb/instance/cmdb_ci/829e953a0ad3370200af63483498b1ea",
                        "value": "829e953a0ad3370200af63483498b1ea"
                    },
                    "type": {
                        "display_value": "Uses::Used by",
                        "link": "https://ven03941.service-now.com/api/now/table/cmdb_rel_type/cb5592603751200032ff8c00dfbe5d17",
                        "value": "cb5592603751200032ff8c00dfbe5d17"
                    }
                }
            ],
            "OutboundRelations": [],
            "SysID": "a8decc3f1b9c2410042611b4bd4bcb7d"
        }
    }
}

Human Readable Output

New relations were added to a8decc3f1b9c2410042611b4bd4bcb7d record successfully

Attributes

Name SysID
Update Name README a8decc3f1b9c2410042611b4bd4bcb7d

Inbound Relations

SysID Target Display Value Type Display Value
b34050bbdb10641026fca015ca961985,
eb3f84331b5c2410042611b4bd4bcbf9
CMS App FLX,
CMS App FLX
Uses::Used by,
Uses::Used by

servicenow-cmdb-record-delete-relations


Delete a relation of an existing record.

Base Command

servicenow-cmdb-record-delete-relations

Input

Argument Name Description Required
class The class name of the record. Required
sys_id The ID of the record from which a relation should be deleted. Required
relation_sys_id The ID of the relation that should be deleted. Required
fields A comma-separated list of the fields to return for the record. Optional
relation_limit The maximum number of relations returned (default: 50). Optional
relation_offset The number of records to exclude from the relations query (default: 0). Optional

Context Output

Path Type Description
ServiceNowCMDB.Record.SysID String The sys_id of the record that was updated.
ServiceNowCMDB.Record.Class String The class name of the record.
ServiceNowCMDB.Record.Attributes Unknown The attributes that were returned in the response for the updated record.
ServiceNowCMDB.Record.OutboundRelations Unknown A list of all the outbound relations of the record.
ServiceNowCMDB.Record.InboundRelations Unknown A list of all the inbound relations of the record.

Command Example

!servicenow-cmdb-record-delete-relations class=cmdb_ci_linux_server relation_sys_id=b376af86dbbf981026fca015ca961981 sys_id=2a41eb4e1b739810042611b4bd4bcb9d

Context Example

{
    "ServiceNowCMDB": {
        "Record": {
            "Attributes": {
                "asset": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/alm_asset/964098b3db50641026fca015ca9619a8",
                    "value": "964098b3db50641026fca015ca9619a8"
                },
                "asset_tag": "",
                "assigned": "",
                "assigned_to": "",
                "assignment_group": "",
                "attributes": "",
                "can_print": "false",
                "category": "Hardware",
                "cd_rom": "false",
                "cd_speed": "",
                "change_control": "",
                "chassis_type": "",
                "checked_in": "",
                "checked_out": "",
                "classification": "Production",
                "comments": "",
                "company": "",
                "correlation_id": "",
                "cost": "",
                "cost_cc": "USD",
                "cost_center": "",
                "cpu_core_count": "",
                "cpu_core_thread": "",
                "cpu_count": "",
                "cpu_manufacturer": "",
                "cpu_name": "",
                "cpu_speed": "",
                "cpu_type": "",
                "default_gateway": "",
                "delivery_date": "",
                "department": "",
                "discovery_source": "ServiceNow",
                "disk_space": "",
                "dns_domain": "",
                "dr_backup": "",
                "due": "",
                "due_in": "",
                "duplicate_of": "",
                "fault_count": "0",
                "firewall_status": "Intranet",
                "first_discovered": "2020-11-12 06:24:49",
                "floppy": "",
                "form_factor": "",
                "fqdn": "",
                "gl_account": "",
                "hardware_status": "installed",
                "hardware_substatus": "",
                "host_name": "",
                "install_date": "",
                "install_status": "1",
                "invoice_number": "",
                "ip_address": "",
                "justification": "",
                "kernel_release": "",
                "last_discovered": "2020-11-12 06:24:49",
                "lease_id": "",
                "location": "",
                "mac_address": "",
                "maintenance_schedule": "",
                "managed_by": "",
                "manufacturer": "",
                "model_id": {
                    "display_value": "Unknown",
                    "link": "https://ven03941.service-now.com/api/now/table/cmdb_model/4ed329e3db27901026fca015ca9619fc",
                    "value": "4ed329e3db27901026fca015ca9619fc"
                },
                "model_number": "",
                "monitor": "false",
                "name": "README Record",
                "object_id": "",
                "operational_status": "1",
                "order_date": "",
                "os": "",
                "os_address_width": "",
                "os_domain": "",
                "os_service_pack": "",
                "os_version": "",
                "owned_by": "",
                "po_number": "",
                "purchase_date": "",
                "ram": "",
                "schedule": "",
                "serial_number": "",
                "short_description": "",
                "skip_sync": "false",
                "start_date": "",
                "subcategory": "Computer",
                "support_group": "",
                "supported_by": "",
                "sys_class_name": "cmdb_ci_linux_server",
                "sys_class_path": "/!!/!2/!(/!!/!0",
                "sys_created_by": "admin",
                "sys_created_on": "2020-11-12 06:24:49",
                "sys_domain": {
                    "display_value": "global",
                    "link": "https://ven03941.service-now.com/api/now/table/sys_user_group/global",
                    "value": "global"
                },
                "sys_domain_path": "/",
                "sys_id": "d64098b3db50641026fca015ca9619a7",
                "sys_mod_count": "0",
                "sys_tags": "",
                "sys_updated_by": "admin",
                "sys_updated_on": "2020-11-12 06:24:49",
                "unverified": "false",
                "used_for": "Production",
                "vendor": "",
                "virtual": "false",
                "warranty_expiration": ""
            },
            "Class": "cmdb_ci_linux_server",
            "InboundRelations": [],
            "OutboundRelations": [],
            "SysID": "2a41eb4e1b739810042611b4bd4bcb9d"
        }
    }
}

Human Readable Output

Deleted relation b376af86dbbf981026fca015ca961981 successfully from 2a41eb4e1b739810042611b4bd4bcb9d record

Attributes

Name SysID
Update Name README 2a41eb4e1b739810042611b4bd4bcb9d

servicenow-cmdb-oauth-login


This function should be used once before running any command when using OAuth authorization.

Base Command

servicenow-cmdb-oauth-login

Input

Argument Name Description Required
username The username that should be used for login. Required
password The password that should be used for login. Required

Context Output

There is no context output for this command.

Command Example

!servicenow-cmdb-oauth-login username=username password=password

Human Readable Output

###Logged in successfully.
A refresh token was saved to the integration context. This token will be used to generate a new access token once the current one expires.

servicenow-cmdb-oauth-test


Test the instance configuration when using OAuth authorization.

Base Command

servicenow-cmdb-oauth-test

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Command Example

!servicenow-cmdb-oauth-test

Human Readable Output

###Instance Configured Successfully.

Configuration parameters

  • url — ServiceNow URL, in the format https://company.service-now.com/ (required)
  • use_oauth — Use OAuth Login
  • use_jwt — Use JWT Authentication
  • credentials — Client ID/JWT Audience (aud)
  • basic_credentials — Username
  • private_key
  • kid — Kid (Key Id)
  • sub — Sub
  • iss — Iss (Issuer)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (8)

  • servicenow-cmdb-oauth-login

    This function should be used once before running any command when using OAuth authorization.

  • servicenow-cmdb-oauth-test

    Test the instance configuration when using OAuth authorization.

  • servicenow-cmdb-record-add-relations

    Add a new relation to an existing record.

  • servicenow-cmdb-record-create

    Create a record with associated relations and attributes.

  • servicenow-cmdb-record-delete-relations

    Delete a relation of an existing record.

  • servicenow-cmdb-record-get-by-id

    Query attributes and relationship information for a specific record.

  • servicenow-cmdb-record-update

    Update a record with the given attributes.

  • servicenow-cmdb-records-list

    Query records for a CMDB class.

import demistomock as demisto
from CommonServerPython import *

from CommonServerUserPython import *

"""IMPORTS"""
import ast
from _collections import defaultdict
from operator import itemgetter
from typing import Any

import urllib3

# Disable insecure warnings
urllib3.disable_warnings()

""" CONSTANTS """
API_VERSION = "/api/now/cmdb/instance/"
CREAT_RECORD_DATA_FIELDS = ["attributes", "inbound_relations", "outbound_relations", "source"]
UPDATE_RECORD_DATA_FIELDS = ["attributes", "source"]
ADD_RELATION_DATA_FIELDS = ["inbound_relations", "outbound_relations", "source"]
FIELD_TO_OUTPUT = {"inbound_relations": "Inbound Relations", "outbound_relations": "Outbound Relations"}


class Client:
    """Client class to interact with the service API

    This Client implements API calls, and does not contain any Demisto logic.
    Should only do requests and return data.
    It inherits from BaseClient defined in CommonServer Python.
    Most calls use _http_request() that handles proxy, SSL verification, etc.
    """

    def __init__(
        self,
        username: str = "",
        password: str = "",
        use_oauth: bool = False,
        client_id: str = "",
        client_secret: str = "",
        url: str = "",
        verify: bool = False,
        proxy: bool = False,
        jwt_params: dict = None,
    ):
        """
        Args:
            - username: the username for authentication.
            - password: the password for authentication.
            - client_id: the client id of the application of the user.
            - client_secret - the client secret of the application of the user.
            - url: the instance url of the user, i.e: https://<instance>.service-now.com.
                   NOTE - url should be given without an API specific suffix as it is also used for the OAuth process.
            - insecure: Whether the request should verify the SSL certificate.
            - proxy: Whether to run the integration using the system proxy.
            - headers: The request headers, for example: {'Accept`: `application/json`}. Can be None.
            - use_oauth: a flag indicating whether the user wants to use OAuth 2.0 or basic authorization.
        """
        headers = {"Content-Type": "application/json", "Accept": "application/json"}
        self.use_oauth = use_oauth
        self.use_jwt = bool(jwt_params)
        self.snow_client: ServiceNowClient = ServiceNowClient(
            username=username,
            password=password,
            use_oauth=use_oauth,
            client_id=client_id,
            client_secret=client_secret,
            url=url,
            verify=verify,
            proxy=proxy,
            headers=headers,
            jwt_params=jwt_params,
        )

    def records_list(self, class_name, params=None):
        return self.snow_client.http_request(method="GET", url_suffix=f"{API_VERSION}{class_name}", params=params)

    def get_record(self, class_name, sys_id, params=None):
        url_suffix = f"{API_VERSION}{class_name}/{sys_id}"
        return self.snow_client.http_request(method="GET", url_suffix=url_suffix, params=params)

    def create_record(self, class_name, data, params=None):
        return self.snow_client.http_request(method="POST", url_suffix=f"{API_VERSION}{class_name}", params=params, data=data)

    def update_record(self, class_name, sys_id, data, params=None):
        url_suffix = f"{API_VERSION}{class_name}/{sys_id}"
        return self.snow_client.http_request(method="PATCH", url_suffix=url_suffix, params=params, data=data)

    def add_relation(self, class_name, sys_id, data, params=None):
        url_suffix = f"{API_VERSION}{class_name}/{sys_id}/relation"
        return self.snow_client.http_request(method="POST", url_suffix=url_suffix, params=params, data=data)

    def delete_relation(self, class_name, sys_id, rel_sys_id, params=None):
        url_suffix = f"{API_VERSION}{class_name}/{sys_id}/relation/{rel_sys_id}"
        return self.snow_client.http_request(method="DELETE", url_suffix=url_suffix, params=params)


""" HELPER FUNCTIONS """


def create_request_data(data_fields: list, args: dict) -> dict:
    """
    This function converts the input given by the user when creating a new record to a data dict that should be passed
    in the http request.

    Args:
        data_fields: A list with the fields that should be added to the data.
        args: The arguments that were filled by the user.

    Returns:
        A dictionary representing the data parameter that should be sent in the http request.
    """
    data = {}
    for field in data_fields:
        if field == "source":
            data[field] = args.get(field)
        elif field == "attributes":  # 'attributes' input should be of the form key1=value1,key2=value2...
            val = args.get(field)
            if val:
                try:
                    attributes_dict = {}
                    attributes_input = val.split(",")
                    for attribute in attributes_input:
                        pair = attribute.split("=")
                        attributes_dict[pair[0]] = pair[1]
                    data[field] = attributes_dict
                except Exception:
                    raise Exception(
                        'Illegal input. Input format should be "key=value". Multiple values can be filled, '
                        "separated by a comma."
                    )
        else:  # other fields should be converted to dict/list
            val = args.get(field)
            if val:
                try:
                    data[field] = ast.literal_eval(val)
                except Exception:
                    raise Exception("Illegal input. Please see the argument description for the correct input format.")
    return data


def create_record_context(class_name: str, sys_id: str, result: dict) -> dict:
    """
    Create the context output for commands that operate on a single record.

    Args:
        class_name: The class name of the record used.
        sys_id: The id of the record.
        result: The raw response from the http request.

    Return:
        A dictionary representing the context output for the record.
    """
    context = {
        "ServiceNowCMDB.Record(val.ID===obj.ID)": {
            "Class": class_name,
            "SysID": sys_id,
            "Attributes": result.get("attributes", {}),
            "InboundRelations": result.get("inbound_relations", []),
            "OutboundRelations": result.get("outbound_relations", []),
        }
    }
    return context


def create_human_readable(title: str, result: dict, fields: str) -> str:
    """
    Create the human readable output for commands.

    Args:
        title: The title of the human readable output.
        result: The raw response from the http request consisting of the attributes, inbound_relations and
                outbound_relations fields.
        fields: A string representing all the fields of the record the client specified that should be returned. If no
        fields were specified, only the record name and sys_id will be displayed in the war room.

    Return:
        A string representing the markdown output that should be displayed in the war room.
    """
    md = f"{title}\n"
    attributes_outputs = {}
    if fields:
        for field in fields.split(","):
            if result.get("attributes", {}).get(field):
                attributes_outputs[string_to_context_key(field)] = result.get("attributes", {}).get(field)
    else:
        attributes_outputs = {
            "SysID": result.get("attributes", {}).get("sys_id"),
            "Name": result.get("attributes", {}).get("name"),
        }

    md += tableToMarkdown("Attributes", t=attributes_outputs, removeNull=True)

    for relation_type in ["inbound_relations", "outbound_relations"]:
        relations = result.get(relation_type)
        if relations:
            relation_output = {
                "SysID": list(map(itemgetter("sys_id"), relations)),
                "Target Display Value": list(
                    map(
                        itemgetter("display_value"),
                        list(map(itemgetter("target"), result.get(relation_type))),  # type: ignore[arg-type]
                    )
                ),  # type: ignore
                "Type Display Value": list(
                    map(
                        itemgetter("display_value"),
                        list(map(itemgetter("type"), result.get(relation_type))),  # type: ignore[arg-type]
                    )
                ),  # type: ignore
            }
            md += f" {tableToMarkdown(FIELD_TO_OUTPUT.get(relation_type), t=relation_output)}"
    return md


""" COMMAND FUNCTIONS """


def records_list_command(client: Client, args: dict) -> tuple[str, dict, Any]:
    """
    Query a CMDB table using the class name to receive all records in the class.

    Args:
        client: Client object with request.
        args: Usually demisto.args()

    Returns:
        Demisto Outputs.
    """
    context: dict = defaultdict(list)
    class_name = args.get("class")
    params = {}
    if args.get("query"):
        params["sysparm_query"] = args.get("query")
    if args.get("limit"):
        params["sysparm_limit"] = args.get("limit")
    if args.get("offset"):
        params["sysparm_offset"] = args.get("offset")

    outputs = {"Class": class_name}

    response = client.records_list(class_name=class_name, params=params)
    result = response.get("result", {})
    if result:
        outputs["Records"] = result
        human_readable = tableToMarkdown(f"Found {len(result)} records for class {class_name}:", t=result)
    else:
        human_readable = f"Found no records for class {class_name}."
    context["ServiceNowCMDB(val.ID===obj.ID)"] = outputs

    return human_readable, context, response


def get_record_command(client: Client, args: dict) -> tuple[str, dict, Any]:
    """
    Query attributes and relationship information for a specific record.

    Args:
        client: Client object with request.
        args: Usually demisto.args()

    Returns:
        Demisto Outputs.
    """
    context: dict = defaultdict(list)
    class_name = args.get("class")
    sys_id = args.get("sys_id")
    params: dict = {}
    if args.get("fields"):
        params["sysparm_fields"] = args.get("fields")
        # Verify that sys_id and name were added so they can be used in the output of the command:
        if "sys_id" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",sys_id"
        if "name" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",name"
    if args.get("relation_limit"):
        params["sysparm_relation_limit"] = args.get("relation_limit")
    if args.get("relation_offset"):
        params["sysparm_relation_offset"] = args.get("relation_offset")

    response = client.get_record(class_name=class_name, sys_id=sys_id, params=params)
    result = response.get("result")
    if result:
        context["ServiceNowCMDB.Record(val.ID===obj.ID)"] = {
            "Class": class_name,
            "SysID": sys_id,
            "Attributes": result.get("attributes", {}),
            "InboundRelations": result.get("inbound_relations", []),
            "OutboundRelations": result.get("outbound_relations", []),
        }
        hr_title = f"### Found the following attributes and relations for record {sys_id}:"
        human_readable = create_human_readable(hr_title, result, params.get("sysparm_fields", ""))
    else:
        context["ServiceNowCMDB.Record(val.ID===obj.ID)"] = {"Class": class_name, "SysID": sys_id}
        human_readable = f"Found no attributes and relations for record {sys_id}."

    return human_readable, context, response


def create_record_command(client: Client, args: dict) -> tuple[str, dict, Any]:
    """
    Create a record with associated relations.

    Args:
        client: Client object with request.
        args: Usually demisto.args()

    Returns:
        Demisto Outputs.
    """
    context: dict = defaultdict(list)
    class_name = args.get("class", "")
    params: dict = {}
    if args.get("fields"):
        params["sysparm_fields"] = args.get("fields")
        # Verify that sys_id and name were added so they can be used in the output of the command:
        if "sys_id" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",sys_id"
        if "name" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",name"
    if args.get("relation_limit"):
        params["sysparm_relation_limit"] = args.get("relation_limit")
    if args.get("relation_offset"):
        params["sysparm_relation_offset"] = args.get("relation_offset")

    data = create_request_data(CREAT_RECORD_DATA_FIELDS, args)

    response = client.create_record(class_name=class_name, params=params, data=str(data))
    result = response.get("result")
    if result:
        sys_id = result.get("attributes", {}).get("sys_id")
        context = create_record_context(class_name, sys_id, result)
        hr_title = f"### Record {sys_id} was created successfully."
        human_readable = create_human_readable(hr_title, result, params.get("sysparm_fields", ""))
    else:
        human_readable = "Failed to create a new record."

    return human_readable, context, response


def update_record_command(client: Client, args: dict) -> tuple[str, dict, Any]:
    """
    Update a record with attributes given by the user.

    Args:
        client: Client object with request.
        args: Usually demisto.args()

    Returns:
        Demisto Outputs.
    """
    context: dict = defaultdict(list)
    class_name = args.get("class", "")
    sys_id = args.get("sys_id", "")
    params: dict = {}
    if args.get("fields"):
        params["sysparm_fields"] = args.get("fields")
        # Verify that sys_id and name were added so they can be used in the output of the command:
        if "sys_id" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",sys_id"
        if "name" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",name"
    if args.get("relation_limit"):
        params["sysparm_relation_limit"] = args.get("relation_limit")
    if args.get("relation_offset"):
        params["sysparm_relation_offset"] = args.get("relation_offset")

    data = create_request_data(UPDATE_RECORD_DATA_FIELDS, args)

    response = client.update_record(class_name=class_name, sys_id=sys_id, data=str(data), params=params)
    result = response.get("result")
    if result:
        context = create_record_context(class_name, sys_id, result)
        hr_title = f"### Updated record {sys_id} successfully."
        human_readable = create_human_readable(hr_title, result, params.get("sysparm_fields", ""))
    else:
        human_readable = f"Failed to update record {sys_id}."

    return human_readable, context, response


def add_relation_command(client: Client, args: dict) -> tuple[str, dict, Any]:
    """
    Add new relations to an existing record.

    Args:
        client: Client object with request.
        args: Usually demisto.args()

    Returns:
        Demisto Outputs.
    """
    context: dict = defaultdict(list)
    class_name = args.get("class", "")
    sys_id = args.get("sys_id", "")
    params: dict = {}
    if args.get("fields"):
        params["sysparm_fields"] = args.get("fields")
        # Verify that sys_id and name were added so they can be used in the output of the command:
        if "sys_id" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",sys_id"
        if "name" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",name"
    if args.get("relation_limit"):
        params["sysparm_relation_limit"] = args.get("relation_limit")
    if args.get("relation_offset"):
        params["sysparm_relation_offset"] = args.get("relation_offset")

    data = create_request_data(ADD_RELATION_DATA_FIELDS, args)

    response = client.add_relation(class_name=class_name, sys_id=sys_id, data=str(data), params=params)
    result = response.get("result")
    if result:
        context = create_record_context(class_name, sys_id, result)
        hr_title = f"### New relations were added to {sys_id} record successfully."
        human_readable = create_human_readable(hr_title, result, params.get("sysparm_fields", ""))
    else:
        human_readable = f"Failed to add new relations to record {sys_id}."

    return human_readable, context, response


def delete_relation_command(client: Client, args: dict) -> tuple[str, dict, Any]:
    """
    Delete relations for an existing record.

    Args:
        client: Client object with request.
        args: Usually demisto.args()

    Returns:
        Demisto Outputs.
    """
    context: dict = defaultdict(list)
    class_name = args.get("class", "")
    sys_id = args.get("sys_id", "")
    rel_sys_id = args.get("relation_sys_id", "")
    params: dict = {}
    if args.get("fields"):
        params["sysparm_fields"] = args.get("fields")
        # Verify that sys_id and name were added so they can be used in the output of the command:
        if "sys_id" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",sys_id"
        if "name" not in params.get("sysparm_fields", ""):
            params["sysparm_fields"] += ",name"
    if args.get("relation_limit"):
        params["sysparm_relation_limit"] = args.get("relation_limit")
    if args.get("relation_offset"):
        params["sysparm_relation_offset"] = args.get("relation_offset")

    response = client.delete_relation(class_name=class_name, sys_id=sys_id, rel_sys_id=rel_sys_id, params=params)
    result = response.get("result")
    if result:
        context = create_record_context(class_name, sys_id, result)
        hr_title = f"### Deleted relation {rel_sys_id} successfully from {sys_id} record."
        human_readable = create_human_readable(hr_title, result, params.get("sysparm_fields", ""))
    else:
        human_readable = f"Failed to delete relation {rel_sys_id} from record {sys_id}."

    return human_readable, context, response


def test_module(client: Client) -> str:
    """Tests API connectivity and authentication'

    Returning 'ok' indicates that the integration works like it is supposed to.
    Connection to the service is successful.
    Raises exceptions if something goes wrong.

    :type client: ``Client``
    :param Client: ServiceNow CMDB client to use

    :return: 'ok' if test passed, anything else will fail the test.
    :rtype: ``str``
    """

    try:
        client.records_list(class_name="cmdb_ci_linux_server")
    except Exception as e:
        raise e
    return "ok"


def oauth_test_module(client: Client, *_) -> tuple[str, dict[Any, Any], dict[Any, Any]]:
    """
    Test the instance configurations when using OAuth authorization.
    """
    if not client.use_oauth:
        return_error(
            "!servicenow-cmdb-oauth-test command should be used only when using OAuth 2.0 authorization.\n "
            "Please select the `Use OAuth Login` checkbox in the instance configuration before running this "
            "command."
        )
    try:
        client.records_list(class_name="cmdb_ci_linux_server")
    except Exception as e:
        raise e
    hr = "### Instance Configured Successfully.\n"
    return hr, {}, {}


def login_command(client: Client, args: dict[str, Any]) -> tuple[str, dict[Any, Any], dict[Any, Any]]:
    """
    Login the user using OAuth authorization
    Args:
        client: Client object with request.
        args: Usually demisto.args()

    Returns:
        Demisto Outputs.
    """
    # Verify that the user selected the `Use OAuth Login` checkbox:
    if not client.use_oauth:
        return_error(
            "!servicenow-cmdb-oauth-login command can be used only when using OAuth 2.0 authorization.\n "
            "Please select the `Use OAuth Login` checkbox in the instance configuration before running this "
            "command."
        )

    username = args.get("username", "")
    password = args.get("password", "")
    try:
        client.snow_client.login(username, password)
        hr = (
            "### Logged in successfully.\n A refresh token was saved to the integration context and will be "
            "used to generate a new access token once the current one expires."
        )
    except Exception as e:
        return_error(
            f"Failed to login. Please verify that the provided username and password are correct, and that you"
            f" entered the correct client id and client secret in the instance configuration (see ? for"
            f"correct usage when using OAuth).\n\n{e}"
        )
    return hr, {}, {}


""" MAIN FUNCTION """


def main() -> None:
    """main function, parses params and runs command functions"""
    params = demisto.params()

    url = params.get("url", "")
    verify = not params.get("insecure", False)
    proxy = params.get("proxy", False)
    client_id = client_secret = ""
    use_oauth = params.get("use_oauth", False)
    use_jwt = params.get("use_jwt", False)
    jwt_params = {}

    basic_auth_creds = params.get("basic_credentials", {})
    username = basic_auth_creds.get("identifier", "")
    password = basic_auth_creds.get("password", "")

    oauth_creds = params.get("credentials", {})

    try:
        # use jwt only with OAuth
        if use_jwt and use_oauth:
            raise ValueError("Please choose only one authentication method (OAuth or JWT).")

        elif use_jwt:
            use_oauth = True

        if use_oauth:
            client_id = oauth_creds.get("identifier", "")
            client_secret = oauth_creds.get("password", "")
        else:
            # if username/password are empty - fallback to legacy which populates the oath credentials
            if not username or not password:
                demisto.debug("Using legacy parameters for username and password")
                username = oauth_creds.get("identifier", "")
                password = oauth_creds.get("password", "")

        if use_jwt:
            if not params.get("private_key") or not params.get("kid") or not params.get("sub"):
                raise Exception("When using JWT, fill private key, kid and sub fields")
            jwt_params = {
                "private_key": params.get("private_key", {}).get("password"),
                "kid": params.get("kid"),
                "sub": params.get("sub"),
                "iss": params.get("iss", client_id),
                "aud": client_id,
            }

        client = Client(
            username=username,
            password=password,
            use_oauth=use_oauth,
            client_id=client_id,
            client_secret=client_secret,
            url=url,
            verify=verify,
            proxy=proxy,
            jwt_params=jwt_params,
        )

        commands = {
            "servicenow-cmdb-oauth-login": login_command,
            "servicenow-cmdb-oauth-test": oauth_test_module,
            "servicenow-cmdb-records-list": records_list_command,
            "servicenow-cmdb-record-get-by-id": get_record_command,
            "servicenow-cmdb-record-create": create_record_command,
            "servicenow-cmdb-record-update": update_record_command,
            "servicenow-cmdb-record-add-relations": add_relation_command,
            "servicenow-cmdb-record-delete-relations": delete_relation_command,
        }

    except Exception as e:
        return_error(f"Error:\n{e!s}")
    try:
        command = demisto.command()
        demisto.debug(f"Command being called is {command}")
        if demisto.command() == "test-module":
            # This is the call made when pressing the integration Test button.
            result = test_module(client)
            return_results(result)
        elif command in commands:
            return_outputs(*commands[command](client, demisto.args()))  # type: ignore
        else:
            return_error("Command not found.")

    # Log exceptions and return errors
    except Exception as e:
        return_error(f"Failed to execute {command} command.\nError:\n{e!s}")


from ServiceNowApiModule import *  # noqa: E402

""" ENTRY POINT """
if __name__ in ("__main__", "__builtin__", "builtins"):
    main()