Ivanti Heat
Use the Ivanti Heat integration to manage issues and create Cortex XSOAR incidents from Ivanti Heat.
Case Management · Ivanti Heat
Details
| ID | Ivanti Heat |
|---|---|
| Provider | Ivanti |
| Category | Case Management |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Ivanti Heat service manager
This integration was integrated and tested with version 2020.1.0.20200313 of Ivanti Heat
Configure Ivanti Heat in Cortex
| Parameter | Description | Required |
|---|---|---|
| url | Server URL (e.g. https://example.net) | True |
| isFetch | Fetch incidents | False |
| incidentType | Incident type | False |
| insecure | Trust any certificate (not secure) | False |
| proxy | Use system proxy settings | False |
| token | API Token | True |
| fetch_time | First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days) | False |
| incident_name_field | The object field to use for incident name | True |
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.
ivanti-heat-objects-list
Fetches business object records based on the defined filter values.
Base Command
ivanti-heat-objects-list
Input
| Argument Name | Description | Required |
|---|---|---|
| rec-id | Buisiness object ID | Optional |
| from | Start date of object records to return in the following format 2012-01-01T00:00:00Z. | Optional |
| to | End date of object records to return in the following format 2012-01-01T00:00:00Z. | Optional |
| limit | The maximum number of object records to return | Optional |
| offset | Starting record index to begin retrieving object records from | Optional |
| search-query | Fetches business object records based on the defined search keyword | Optional |
| object-type | Type of object record | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IvantiHeat.incidents.RecId | String | Incident ID |
| IvantiHeat.incidents.Subject | String | Incident subject |
| IvantiHeat.incidents.Status | String | Incident status |
| IvantiHeat.incidents.CreatedDateTime | Date | Incident createion time |
| IvantiHeat.incidents.Symptom | String | Incident description |
| IvantiHeat.incidents.OwnerTeam | String | Incident owner team |
| IvantiHeat.incidents.IncidentNumber | Number | The incident number |
| IvantiHeat.incidents.CreatedBy | String | The user who created the incident |
| IvantiHeat.incidents.Owner | String | Incident owner |
| IvantiHeat.incidents.Category | String | Incident category |
| IvantiHeat.incidents.Priority | Number | Incident priority |
| IvantiHeat.incidents.TypeOfIncident | String | Incident type |
| IvantiHeat.incidents.ActualCategory | String | Incident actual category |
| IvantiHeat.incidents.SocialTextHeader | String | Incident text header |
| IvantiHeat.incidents.Email | String | Incident owner Email |
Command Example
!ivanti-heat-objects-list object-type=problems search-query=email from=2011-09-01 to=2012-01-01
Context Example
{
"IvantiHeat": {
"problems": {
"Category": "Accessibility",
"Category_Valid": "48686D",
"ClosedBy": null,
"ClosedDateTime": "2020-07-01T08:42:33Z",
"Cost": 188,
"CreatedBy": "johnny cash",
"CreatedDateTime": "2011-09-18T23:42:38Z",
"Description": "Email down",
"Environment": "",
"Environment_Valid": "",
"ErrorMessage": "",
"FirstIncidentLinkedDateTime": null,
"Impact": "Low",
"Impact_Valid": "0ABE7B3BC5E9B19B258",
"IncidentToRootCauseDuration": 277203595,
"IsInFinalState": false,
"IsUnRead": false,
"IsWorkAround": false,
"KPIAfterIdentification": 0,
"KPIBeforeIdentification": 0,
"KPITimeUntilRootCause": 0,
"KnownErrorDate": "2011-09-18T23:44:37Z",
"KnownErrorDuration": 0,
"LastModBy": "Admin",
"LastModDateTime": "2020-07-01T08:42:33Z",
"OrganizationalUnit": "",
"OrganizationalUnit_Valid": "",
"Owner": "ADale",
"OwnerEmailAddress": "user@domain.com",
"OwnerTeam": "Problem Management",
"OwnerTeam_Valid": "2D8B665640EC10B522A",
"Owner_Valid": "C19505062E42B83CB",
"Priority": "4",
"Priority_Valid": "DD1B90A",
"ProblemDuration": 0,
"ProblemLifetime": 0,
"ProblemNumber": 10053,
"ReadOnly": false,
"RecId": "83837179C323966",
"Resolution": "I cant see my emails",
"ResolutionAction": "Request for Change",
"ResolutionAction_Valid": "87D63706706DBE12A1",
"ResolutionEscLink": "",
"ResolutionEscLink_Category": "",
"ResolutionEscLink_RecID": "",
"ResponseEscalationLink": "",
"ResponseEscalationLink_Category": "",
"ResponseEscalationLink_RecID": "",
"RootCause": "Missing DLL",
"RootCauseDateTimeCreated": "2011-09-22T23:42:38Z",
"Service": "",
"Service_Valid": "",
"SocialTextHeader": "Problem 103: Email down",
"Source": "Incident Management",
"Source_Valid": "A7D73C90FB4AA0D549FF03C36",
"Status": "Resolved",
"Status_Valid": "6C9D23834",
"Subject": "Email down",
"TargetResolutionTime": null,
"TotalTimeSpent": 208,
"TotalWaitingDuration": 0,
"TypeOfProblem": "Known Error",
"TypeOfProblem_Valid": "50075354BCAAE1",
"Urgency": "Medium",
"Urgency_Valid": "44021B4B3306053AE",
"WaitingEscLink": "",
"WaitingEscLink_Category": "",
"WaitingEscLink_RecID": "",
"Workaround": ""
}
}
}
Human Readable Output
problems results
RecId Subject Status CreatedDateTime Urgency OwnerTeam CreatedBy Owner Category Description Priority ClosedDateTime SocialTextHeader 8383718895204FFBB3EC95E79C323966 Email down Resolved 2011-09-18T23:42:38Z Medium Problem Management johnny cash owner Accessibility Email down 4 2020-07-01T08:42:33Z Problem 103: Email down
ivanti-heat-object-update
Update details of a business object such as a change, problem, or incident.
Base Command
ivanti-heat-object-update
Input
| Argument Name | Description | Required |
|---|---|---|
| rec-id | Buisiness object ID to update | Required |
| fields | Fields values in json format to update in the record, e.g: {“Priority”:5} | Required |
| object-type | Type of object record | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IvantiHeat.incidents.RecId | String | Incident ID |
| IvantiHeat.incidents.Subject | String | Incident subject |
| IvantiHeat.incidents.Status | String | Incident status |
| IvantiHeat.incidents.CreatedDateTime | Date | Incident createion time |
| IvantiHeat.incidents.Symptom | String | Incident description |
| IvantiHeat.incidents.OwnerTeam | String | Incident owner team |
| IvantiHeat.incidents.IncidentNumber | Number | The incident number |
| IvantiHeat.incidents.CreatedBy | String | The user who created the incident |
| IvantiHeat.incidents.Owner | String | Incident owner |
| IvantiHeat.incidents.Category | String | Incident category |
| IvantiHeat.incidents.Priority | Number | Incident priority |
| IvantiHeat.incidents.TypeOfIncident | String | Incident type |
| IvantiHeat.incidents.ActualCategory | String | Incident actual category |
| IvantiHeat.incidents.SocialTextHeader | String | Incident text header |
| IvantiHeat.incidents.Email | String | Incident owner Email |
Command Example
!ivanti-heat-object-update fields={"Description":desc","Priority":1} object-type=problems rec-id=5874A667
Context Example
{
"IvantiHeat": {
"problems": {
"@odata.context": "https://*ivanti-host*/api/odata/$metadata#problems/$entity",
"Category": "Applications",
"Category_Valid": "2E3A3EC0",
"ClosedBy": null,
"ClosedDateTime": "2020-07-05T12:28:12Z",
"Cost": 48,
"CreatedBy": "Admin",
"CreatedDateTime": "2012-10-22T23:35:21Z",
"Description": "desc",
"Environment": "",
"Environment_Valid": "",
"ErrorMessage": "File format not supported",
"FirstIncidentLinkedDateTime": "2015-06-10T21:26:17Z",
"Impact": "Medium",
"Impact_Valid": "1AFFC174C7EA4AB79CCA6B15EB67006D",
"IncidentToRootCauseDuration": 243003171,
"IsInFinalState": false,
"IsUnRead": false,
"IsWorkAround": false,
"KPIAfterIdentification": 0,
"KPIBeforeIdentification": 0,
"KPITimeUntilRootCause": 0,
"KnownErrorDate": null,
"KnownErrorDuration": 0,
"LastModBy": "Admin",
"LastModDateTime": "2020-07-05T12:28:12Z",
"OrganizationalUnit": "",
"OrganizationalUnit_Valid": "",
"Owner": "ADale",
"OwnerEmailAddress": "user@domain.com",
"OwnerTeam": "Problem Management",
"OwnerTeam_Valid": "2D8BB522A",
"Owner_Valid": "C195083CB",
"Priority": "1",
"Priority_Valid": "1F2B8",
"ProblemDuration": 0,
"ProblemLifetime": 0,
"ProblemNumber": 106,
"ReadOnly": false,
"RecId": "588467A667",
"Resolution": "File format not supported",
"ResolutionAction": "Vendor Fix",
"ResolutionAction_Valid": "443D4AB",
"ResolutionEscLink": "",
"ResolutionEscLink_Category": "",
"ResolutionEscLink_RecID": "",
"ResponseEscalationLink": "",
"ResponseEscalationLink_Category": "",
"ResponseEscalationLink_RecID": "",
"RootCause": "",
"RootCauseDateTimeCreated": "2012-11-07T04:35:27Z",
"Service": "",
"Service_Valid": "",
"SocialTextHeader": "Problem 106: Unable to open Office 2007/2010 files",
"Source": "Incident Management",
"Source_Valid": "A76",
"Status": "Resolved",
"Status_Valid": "6C934",
"Subject": "Unable to open Office 2007/2010 files",
"TargetResolutionTime": null,
"TotalTimeSpent": 377,
"TotalWaitingDuration": 0,
"TypeOfProblem": "Problem",
"TypeOfProblem_Valid": "B14E72CCA804",
"Urgency": "Medium",
"Urgency_Valid": "44021B6053AE",
"WaitingEscLink": "",
"WaitingEscLink_Category": "",
"WaitingEscLink_RecID": "",
"Workaround": ""
}
}
}
Human Readable Output
54A667 updated successfully
RecId Subject Status CreatedDateTime Urgency OwnerTeam CreatedBy Owner Category Description Priority ClosedDateTime SocialTextHeader 54A667 Unable to open Office 2007/2010 files Resolved 2012-10-22T23:35:21Z Medium Problem Management Admin johnny cash Applications desc 1 2020-07-05T12:28:12Z Problem 106: Unable to open Office 2007/2010 files
ivanti-heat-object-delete
Delete a business object such as a change, problem, or incident by its Record ID.
Base Command
ivanti-heat-object-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| rec-id | Buisiness object ID | Required |
| object-type | Type of object record | Required |
Context Output
There is no context output for this command.
Command Example
!ivanti-heat-object-delete object-type=incidents rec-id=490C3
Context Example
{}
Human Readable Output
Record 490C3 deleted successfully
ivanti-heat-object-attachment-download
Get attachments from business objects by attachment ID.
Base Command
ivanti-heat-object-attachment-download
Input
| Argument Name | Description | Required |
|---|---|---|
| attachment-id | The attachment ID | Required |
Context Output
There is no context output for this command.
Command Example
!ivanti-heat-object-attachment-download attachment-id=C03CE103827944E59A4EC23498EA9C6A
Context Example
{
"File": {
"EntryID": "1807ab951",
"Extension": "txt",
"Info": "text/plain; charset=utf-8",
"MD5": "5d1017c592",
"Name": "a.txt",
"SHA1": "aaf4c6182cd9aea9434d",
"SHA256": "2cf24dba24",
"SHA512": "9b71d224bd62cf73bcdec043",
"SSDeep": "3:iKn:p",
"Size": 5,
"Type": "ASCII text, with no line terminators"
}
}
Human Readable Output
ivanti-heat-object-attachment-upload
Upload attachments to business objects.
Base Command
ivanti-heat-object-attachment-upload
Input
| Argument Name | Description | Required |
|---|---|---|
| entry-id | The entry ID of the file in XSOAR’s context | Required |
| rec-id | Buisiness object ID | Required |
| object-type | Type of object record | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IvantiHeat.Attachment.AttachmentId | String | The ID of the attachment |
| IvantiHeat.Attachment.FileName | String | The name of the attachment |
| IvantiHeat.Attachment.RecId | String | Attachement buisiness object ID |
Command Example
!ivanti-heat-object-attachment-upload object-type=problem rec-id=D14D995B entry-id=18ab951
Context Example
{
"IvantiHeat": {
"Attachment": {
"AttachmentId": "A3039BF750",
"FileName": "11.jpg",
"RecId": "D14D995B"
}
}
}
Human Readable Output
11.jpg uploaded successfully, attachment ID: A3039BF750
ivanti-heat-object-perform-action
Performs quick actions for a business object. For example, close, clone or resolve an incident or a problem.
Base Command
ivanti-heat-object-perform-action
Input
| Argument Name | Description | Required |
|---|---|---|
| object-type | Type of object record | Required |
| object-id | Buisiness object ID | Required |
| action | The action to perform | Required |
| request-data | The request body in JSON format | Required |
Context Output
There is no context output for this command.
Command Example
!ivanti-heat-object-perform-action action=Close_Incident object-id=123ABC object-type=incidents request-data=${ivantiHeat.CloseIncidentJSON}
Human Readable Output
Close_Incident action success
ivanti-heat-object-create
create business objects available out-of-the-box, such as a Change, Problem, Incident, or any custom defined business object of your choice.
Base Command
ivanti-heat-object-create
Input
| Argument Name | Description | Required |
|---|---|---|
| object-type | Type of object record | Required |
| fields | The request body in JSON format, or using script for create the request payload e.g IvantiHeatCloseIncidentExample | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IvantiHeat.incidents.RecId | String | Incident ID |
| IvantiHeat.incidents.Subject | String | Incident subject |
| IvantiHeat.incidents.Status | String | Incident status |
| IvantiHeat.incidents.CreatedDateTime | Date | Incident createion time |
| IvantiHeat.incidents.Symptom | String | Incident description |
| IvantiHeat.incidents.OwnerTeam | String | Incident owner team |
| IvantiHeat.incidents.IncidentNumber | Number | The incident number |
| IvantiHeat.incidents.CreatedBy | String | The user who created the incident |
| IvantiHeat.incidents.Owner | String | Incident owner |
| IvantiHeat.incidents.Category | String | Incident category |
| IvantiHeat.incidents.Priority | Number | Incident priority |
| IvantiHeat.incidents.TypeOfIncident | String | Incident type |
| IvantiHeat.incidents.ActualCategory | String | Incident actual category |
| IvantiHeat.incidents.SocialTextHeader | String | Incident text header |
| IvantiHeat.incidents.Email | String | Incident owner Email |
Command Example
!ivanti-heat-object-create object-type=incidents fields=`{"Category":"Connectivity","ProfileLink":"1087E597","Symptom":"the description","Subject":"test incident","Service":"Desktop Service","Owner":"johnny cash"}`
Context Example
{
"IvantiHeat": {
"incidents": {
"@odata.context": "https://*ivanti-host*/api/odata/$metadata#incidents/$entity",
"ActualCategory": "Connectivity",
"ActualCategory_Valid": null,
"ActualService": "Desktop Service",
"ActualService_Valid": "164A9DCC1A",
"AlternateContactEmail": null,
"AlternateContactLink": null,
"AlternateContactLink_Category": null,
"AlternateContactLink_RecID": null,
"AlternateContactPhone": null,
"ApprovalStatus": null,
"Approver": "user@domain.com",
"Approver_Valid": "2F8539C7",
"Category": "Connectivity",
"Category_Valid": "6013B2C7",
"CauseCode": null,
"CauseCode_Valid": null,
"ClosedBy": null,
"ClosedDateTime": null,
"ClosedDuration": 0,
"ClosingEscLink": null,
"ClosingEscLink_Category": null,
"ClosingEscLink_RecID": null,
"Cost": "0.0000",
"CostPerMinute": "0.4000",
"CostPerMinute_Currency": "USD",
"CostPerMinute_CurrencyValid": null,
"Cost_Currency": "USD",
"Cost_CurrencyValid": null,
"CreatedBy": "Admin",
"CreatedByType": "Web Client",
"CreatedDateTime": "2020-07-05T12:28:16Z",
"CustomerDepartment": "IT",
"CustomerLocation": "USA",
"CustomerLocation_Valid": "A4DD2BCF246A",
"Email": "user@domain.com",
"EntityLink": "4A051AD111",
"EntityLink_Category": "OrganizationalUnit",
"EntityLink_RecID": "4A05123D6AD111",
"EventCIRecId": null,
"FirstCallResolution": false,
"HRCaseLink": null,
"HRCaseLink_Category": null,
"HRCaseLink_RecID": null,
"HoursOfOperation": "Weekly HOP",
"HoursOfOperation_Valid": "FF57246B2E0047D193C1AEC1011D746B",
"Impact": null,
"Impact_Valid": null,
"IncidentDetailSummary": null,
"IncidentDetailWorkflowTag": null,
"IncidentNetworkUserName": "jcash",
"IncidentNumber": 112,
"IsApprovalNeeded": false,
"IsDSMTaskExisted": false,
"IsInFinalState": false,
"IsMasterIncident": false,
"IsNewRecord": false,
"IsNotification": true,
"IsReclassifiedForResolution": false,
"IsRelatedIncidentResolutionUpdate": false,
"IsRelatedIncidentUpdate": false,
"IsReportedByAlternateContact": false,
"IsResolvedByMaster": false,
"IsUnRead": false,
"IsVIP": false,
"IsWorkAround": false,
"KnowledgeLink": null,
"KnowledgeLink_Category": null,
"KnowledgeLink_RecID": null,
"LastModBy": "Admin",
"LastModDateTime": "2020-07-05T12:28:16Z",
"LoginId": "ATaylor",
"MasterIncidentLink": null,
"MasterIncidentLink_Category": null,
"MasterIncidentLink_RecID": null,
"NewNotes": null,
"OrgUnitLink": "4A0AD111",
"OrgUnitLink_Category": "OrganizationalUnit",
"OrgUnitLink_RecID": "4A05123D611",
"OrganizationUnitID": "GMI",
"Owner": "ATaylor",
"OwnerEmail": "user@domain.com",
"OwnerTeam": "Service Desk",
"OwnerTeamEmail": "user@domain.com",
"OwnerTeam_Valid": "2E4BABF0D9B80C47",
"OwnerType": "Employee",
"Owner_Valid": "108452E597",
"OwnershipAssignmentEmail": "user@domain.com",
"OwningOrgUnitId": "GMI",
"OwningOrgUnitId_Valid": "4A0AD111",
"Phone": "+1 22.33.44",
"PreviousState": null,
"Priority": "3",
"Priority_Valid": "29CD5096",
"ProblemLink": null,
"ProblemLink_Category": null,
"ProblemLink_RecID": null,
"ProfileFullName": "johnny cash",
"ProfileLink": "108734597",
"ProfileLink_Category": "Employee",
"ProfileLink_RecID": "10873D64B452E597",
"ProgressBarPosition": "3",
"ReadOnly": false,
"RecId": "FDF098DE",
"ReportedBy": null,
"ReportingOrgUnitID": null,
"ReportingOrgUnitID_Valid": null,
"Resolution": null,
"ResolutionEscLink": null,
"ResolutionEscLink_Category": null,
"ResolutionEscLink_RecID": null,
"ResolvedBy": null,
"ResolvedByIncidentNumber": 0,
"ResolvedByType": null,
"ResolvedDateTime": null,
"RespondedBy": null,
"RespondedDateTime": null,
"ResponseEscLink": null,
"ResponseEscLink_Category": null,
"ResponseEscLink_RecID": null,
"SLA": "",
"SLADisplayText": "",
"SLALink": null,
"SLALink_Category": null,
"SLALink_RecID": null,
"SendSurveyNotification": false,
"Service": "Desktop Service",
"ServiceOwnerEmail": null,
"ServiceReqLink": null,
"ServiceReqLink_Category": null,
"ServiceReqLink_RecID": null,
"Service_Valid": "164A2AC1A",
"SocialTextHeader": "Incident 11152: test incident",
"Source": "Phone",
"Source_Valid": "EF78045C",
"Status": "Active",
"Status_Valid": "EAB221AA",
"Subcategory": null,
"Subcategory_Valid": null,
"Subject": "test incident",
"Symptom": "the description",
"TeamManagerEmail": "user@domain.com",
"TotalTimeSpent": 0,
"TypeOfIncident": "Failure",
"Urgency": null,
"Urgency_Valid": null,
"ViewType": null,
"WaitingEscLink": null,
"WaitingEscLink_Category": null,
"WaitingEscLink_RecID": null,
"helpdesk_Priority": null,
"helpdesk_Priority_Valid": null
}
}
}
Human Readable Output
incidents object created successfully
RecId Subject Status CreatedDateTime Symptom OwnerTeam IncidentNumber CreatedBy Owner Category Priority TypeOfIncident ActualCategory SocialTextHeader FDF08798DE test incident Active 2020-07-05T12:28:16Z the description Service Desk 152 Admin jcash Connectivity 3 user@domain.com Failure Connectivity Incident 112: test incident
Configuration parameters
url— Server URL (e.g. https://example.net) (required)token— API Tokentoken_creds—isFetch— Fetch incidentsincidentType— Incident typeincidentFetchInterval— Incidents Fetch Intervalfetch_time— First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)incident_name_field— The object field to use for incident name (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (7)
-
ivanti-heat-object-attachment-downloadGets attachments from business objects by attachment ID.
-
ivanti-heat-object-attachment-uploadUploads attachments to business objects.
-
ivanti-heat-object-createCreates business objects that are available out-of-the-box, such as a Change, Problem, Incident, or any custom defined business object.
-
ivanti-heat-object-deleteDeletes a business object by record ID, such as a change, problem, or incident.
-
ivanti-heat-object-perform-actionPerforms quick actions on a business object. For example, close, clone, or resolve an incident or a problem.
-
ivanti-heat-object-updateUpdates details of a business object, such as a change, problem, or incident.
-
ivanti-heat-objects-listFetches business object records based on the defined filter values.
import demistomock as demisto from CommonServerPython import * from CommonServerUserPython import * """ IMPORTS """ import json import dateparser import urllib3 urllib3.disable_warnings() """ CONSTANTS """ DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" OBJECT_HEADERS = [ "RecId", "Subject", "Name", "Status", "CreatedDateTime", "Symptom", "Urgency", "OwnerTeam", "IncidentNumber", "CreatedBy", "Details", "Owner", "Category", "Description", "Priority", "Email", "TypeOfIncident", "ClosedDateTime", "ActualCategory", "SocialTextHeader", ] class Client(BaseClient): """ Client will implement the service API, and should not contain any Demisto logic. Should only do requests and return data. """ def do_request(self, method, url_suffix, params=None, data=None, json_data=None, resp_type="json", files=None): res = self._http_request( method, url_suffix, params=params, data=data, json_data=json_data, files=files, resp_type=resp_type, ok_codes=(200, 201, 204), return_empty_response=True, ) if isinstance(res, dict | list) or resp_type == "other": return res return {} def test_module(client): first_fetch_time = demisto.params().get("fetch_time", "3 days").strip() if demisto.params().get("isFetch", False): # Validate fetch_time parameter is valid (if not, parse_date_range will raise the error message) parse_date_range(first_fetch_time, "%Y-%m-%d %H:%M:%S") client.do_request("GET", "odata/businessobject/incidents") return "ok" def generate_filter_param(rec_id, from_date, to_date): if rec_id: return f"recId eq '{rec_id}'" if not from_date and not to_date: return "" if from_date and not to_date: return f"CreatedDateTime gt {from_date}" if to_date and not from_date: return f"CreatedDateTime lt {to_date}" return f"CreatedDateTime gt {from_date} and CreatedDateTime lt {to_date}" @logger def objects_list_command(client, args): object_type = args.get("object-type") rec_id = args.get("rec-id") from_date = args.get("from") to_date = args.get("to") limit = args.get("limit") offset = args.get("offset") query = args.get("search-query") params = {"$orderby": "CreatedDateTime desc"} filter_txt = generate_filter_param(rec_id, from_date, to_date) if filter_txt: params["$filter"] = filter_txt if limit: params["$top"] = limit if offset: params["$skip"] = offset if query: params["$search"] = query raw_res = client.do_request("GET", f"odata/businessobject/{object_type}", params=params) if raw_res.get("value"): human_readable = tableToMarkdown( f"{object_type} results", t=raw_res.get("value"), headers=OBJECT_HEADERS, removeNull=True ) entry_context = {f"IvantiHeat.{object_type}(val.RecId && val.RecId===obj.RecId)": raw_res.get("value")} return human_readable, entry_context, raw_res return "No records found", {}, raw_res @logger def update_object_command(client, args): object_type = args.get("object-type") rec_id = args.get("rec-id") fields = args.get("fields") try: fields = json.loads(fields) except Exception: raise Exception(f"Failed to parse fields as JSON data, received object:\n{fields}") raw_res = client.do_request("PUT", f"odata/businessobject/{object_type}('{rec_id}')", json_data=fields) human_readable = tableToMarkdown(f"{rec_id} updated successfully", t=raw_res, headers=OBJECT_HEADERS, removeNull=True) entry_context = {f"IvantiHeat.{object_type}(val.RecId===obj.RecId)": raw_res} return human_readable, entry_context, raw_res @logger def delete_object_command(client, args): object_type = args.get("object-type") rec_id = args.get("rec-id") raw_res = client.do_request("DELETE", f"odata/businessobject/{object_type}('{rec_id}')") return f"Record {rec_id} deleted successfully", {}, raw_res @logger def get_attachment_command(client, args): attachment_id = args.get("attachment-id") raw_res = client.do_request("Get", f"rest/Attachment?ID={attachment_id}", resp_type="other") filename_header = raw_res.headers.get("content-disposition") f_attr = "filename=" if filename_header and f_attr in filename_header: # noqa: RET503 filename = filename_header[filename_header.index(f_attr) + len(f_attr) :] return fileResult(filename, raw_res.content) @logger def upload_attachment_command(client, args): object_type = args.get("object-type") entry_id = args.get("entry-id") rec_id = args.get("rec-id") # Validate all required arguments are present and non-empty for arg_name, arg_value in (("object-type", object_type), ("entry-id", entry_id), ("rec-id", rec_id)): if arg_value is None or (isinstance(arg_value, str) and not arg_value.strip()): raise ValueError( f'Required argument "{arg_name}" is missing or empty. ' "Verify the calling automation provides all of: object-type, entry-id, rec-id." ) body = {"ObjectID": rec_id, "ObjectType": f"{object_type}#"} demisto.debug(f"[debug] resolving file for {entry_id=}") try: file_tuple = get_file(entry_id) except Exception as exc: raise DemistoException( f'Failed to resolve attachment file for entry-id="{entry_id}". ' "Verify the entry ID points to an existing file in the war room. " f"Underlying error: {exc}" ) from exc raw_res = client.do_request("POST", "rest/Attachment", data=body, files={"file": file_tuple}) if raw_res: attachment = raw_res[0] attachment_id = attachment.get("Message") file_name = attachment.get("FileName") entry_context = {"IvantiHeat.Attachment": {"RecId": rec_id, "AttachmentId": attachment_id, "FileName": file_name}} return f"{file_name} uploaded successfully, attachment ID: {attachment_id}", entry_context, raw_res raise Exception(f"Upload attachment {rec_id} failed") @logger def perform_action_command(client, args): object_type = args.get("object-type") object_id = args.get("object-id") action = args.get("action") request_data = args.get("request-data") try: request_data = json.loads(request_data) except Exception: raise Exception(f"Failed to parse request-data as JSON data, received object:\n{request_data}") raw_res = client.do_request( "POST", f"odata/businessobject/{object_type}('{object_id}')/{action}", json_data=request_data, resp_type="other" ) return f"{action} action success", {}, raw_res.content @logger def create_object_command(client, args): object_type = args.get("object-type") fields = args.get("fields") body = {} try: fields = json.loads(fields) except Exception: raise Exception(f"Failed to parse additional-fields as JSON data, received object:\n{fields}") for key in fields: body[key] = fields[key] raw_res = client.do_request("POST", f"odata/businessobject/{object_type}", json_data=body) human_readable = tableToMarkdown( f"{object_type} object created successfully", t=raw_res, headers=OBJECT_HEADERS, removeNull=True ) entry_context = {f"IvantiHeat.{object_type}(val.RecId===obj.RecId)": raw_res} return human_readable, entry_context, raw_res def get_file(entry_id): get_file_path_res = demisto.getFilePath(entry_id) file_path = get_file_path_res["path"] file_name = get_file_path_res["name"] with open(file_path, "rb") as fopen: file_bytes = fopen.read() return file_name, file_bytes @logger def fetch_incidents(client, last_run, first_fetch_time, name_field): # Get the last fetch time, if exists last_fetch = last_run.get("last_fetch") # Handle first time fetch if last_fetch is None: last_fetch = dateparser.parse(first_fetch_time) else: last_fetch = dateparser.parse(last_fetch) incidents = [] params = {"$orderby": "CreatedDateTime asc"} params["$filter"] = f"CreatedDateTime gt {last_fetch.strftime(DATE_FORMAT)}" raw_res = client.do_request("GET", "odata/businessobject/incidents", params=params) for item in raw_res.get("value", []): incident_created_time = dateparser.parse(item.get("CreatedDateTime")) assert incident_created_time is not None incident_name = item.get(name_field, item.get("RecId")) incident = { "name": incident_name, "details": json.dumps(item), "occurred": incident_created_time.strftime("%Y-%m-%dT%H:%M:%SZ"), "rawJSON": json.dumps(item), } incidents.append(incident) last_fetch = incident_created_time next_run = {"last_fetch": last_fetch.strftime(DATE_FORMAT)} return next_run, incidents def main(): """ PARSE AND VALIDATE INTEGRATION PARAMS """ # get the service API url base_url = urljoin(demisto.params()["url"], "/api") token = demisto.params().get("token_creds", {}).get("password") or demisto.params().get("token") verify_certificate = not demisto.params().get("insecure", False) # How much time before the first fetch to retrieve incidents first_fetch_time = demisto.params().get("fetch_time", "3 days").strip() proxy = demisto.params().get("proxy", False) commands = { "ivanti-heat-objects-list": objects_list_command, "ivanti-heat-object-update": update_object_command, "ivanti-heat-object-delete": delete_object_command, "ivanti-heat-object-attachment-upload": upload_attachment_command, "ivanti-heat-object-create": create_object_command, "ivanti-heat-object-perform-action": perform_action_command, } LOG(f"Command being called is {demisto.command()}") try: client = Client( base_url=base_url, verify=verify_certificate, headers={"Authorization": f"rest_api_key={token}"}, proxy=proxy ) if demisto.command() == "test-module": # This is the call made when pressing the integration Test button. result = test_module(client) demisto.results(result) elif demisto.command() == "ivanti-heat-object-attachment-download": demisto.results(get_attachment_command(client, demisto.args())) elif demisto.command() == "fetch-incidents": # Set and define the fetch incidents command to run after activated via integration settings. next_run, incidents = fetch_incidents( client=client, last_run=demisto.getLastRun(), first_fetch_time=first_fetch_time, name_field=demisto.params().get("incident_name_field"), ) demisto.setLastRun(next_run) demisto.incidents(incidents) else: hr, outputs, raw = commands[demisto.command()](client, demisto.args()) return_outputs(hr, outputs, raw) # Log exceptions except Exception as e: return_error(f"Failed to execute {demisto.command()} command. Error: {e!s}") if __name__ in ("__main__", "__builtin__", "builtins"): main()