OTRS
Service management suite that comprises ticketing, workflow automation, and notification.
Case Management · OTRS Service Management XSOAR Pack
Details
| ID | OTRS |
|---|---|
| Provider | EasyVista |
| Category | Case Management |
| From Version | 5.0.0 |
| Docker Image | demisto/pyotrs:1.0.0.10133006 |
| Supported Modules | Agentix XSIAM |
README
Service management suite that comprises ticketing, workflow automation, and notification.
This integration was integrated and tested with OTRS versions 5, 6, and 7.
Prerequisite
Before configuring OTRS on Cortex XSOAR, you need to enable the webservices in your OTRS instance. It is recommended to use the provided YAML webservice configuration template, which includes the Route: /TicketList endpoint required for PyOTRS but which is not included in the default OTRS webservice setup. If you use a different file than the template, make sure to name your file GenericTicketConnectorREST.yml.
This integration uses an optional JSON routing table corresponding to the aforementioned webservice configuration template which defaults to this JSON value in PyOTRS. The parameter webservice_config_ticket may be used to supply a different JSON configuration if your instance implements other endpoints or expects other outputs (e.g. changing the Result of SessionGet to AccessToken in newer versions).
Configure OTRS on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for OTRS.
- Click Add instance to create and configure a new integration instance.
| Parameter | Description | Required |
|---|---|---|
| server | OTRS Server URL (for example http://example.com ) | True |
| credentials | OTRS Credentials | True |
| unsecure | Trust any certificate (not secure) | False |
| proxy | Use system proxy settings | False |
| isFetch | Fetch incidents | False |
| incidentType | Incident type | False |
| fetch_queue | Queues to fetch tickets from ("Any" fetches from all queues. CSV supported, for example Misc, Raw) | False |
| fetch_priority | Fetch tickets in priority | False |
| fetch_time | First fetch timestamp (formatted as <number> <time unit>, for example 12 hours, 7 days, 3 months, 1 year) | False |
| look_back | Days to look back when fetching | False |
| webservice_config_ticket | JSON representation of the webservice configuration for tickets as described in the Prerequisite section | False |
- Click Test to validate the URLs, token, and connection.
Commands
You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
otrs-get-ticket
Retrieves details for an OTRS ticket by ticket ID or ticket number. At least one input argument is required for the integration to run.
Base Command
otrs-get-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | Ticket ID of the ticket to get details for. If not spcecified, the ticket_number argument is required. | Optional |
| ticket_number | Ticket Number of the ticket to get details for. If not specified, the ticket_id argument is required. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| OTRS.Ticket.ID | string | Ticket ID |
| OTRS.Ticket.Number | string | Ticket number |
| OTRS.Ticket.Created | date | Ticket creation date |
| OTRS.Ticket.CustomerUser | string | Customer user related to the ticket |
| OTRS.Ticket.Owner | string | Ticket owner |
| OTRS.Ticket.Priority | string | Ticket priority |
| OTRS.Ticket.Queue | string | Queue the ticket is in |
| OTRS.Ticket.State | string | Ticket state |
| OTRS.Ticket.Title | string | Ticket title |
| OTRS.Ticket.Type | string | Ticket type |
| OTRS.Ticket.DynamicField | string | Ticket dynamic fields |
| OTRS.Ticket.Article.Subject | string | Ticket article subject |
| OTRS.Ticket.Article.Body | string | Ticket article body |
| OTRS.Ticket.Article.CreatedTime | date | Ticket article creation time |
| OTRS.Ticket.Article.ContentType | string | Ticket article content type |
| OTRS.Ticket.Article.From | string | Ticket article sender |
| OTRS.Ticket.Article.ID | string | Ticket article ID |
| OTRS.Ticket.Article.Attachment.Name | string | Ticket article attachment file name |
| OTRS.Ticket.Article.Attachment.Size | number | Ticket article attachment file size |
| OTRS.Ticket.Article.Attachment.ContentType | string | Ticket article attachment file content type |
| OTRS.Ticket.Lock | string | Is the ticket locked or unlocked |
| File.Size | number | Size of the file attachment |
| File.SHA1 | string | SHA-1 of the file attachment |
| File.SHA256 | string | SHA-256 of the file attachment |
| File.Name | string | Attachment file name |
| File.SSDeep | string | Attachment file SSDeep |
| File.EntryID | string | Attachment file entry ID |
| File.Info | string | Attachment file information |
| File.Type | string | Attachment file type |
| File.MD5 | string | Attachment file MD5 |
| File.Extension | string | Attachment file extension |
Command Example
!otrs-get-ticket ticket_id="7023"
Context Example
{
"OTRS": {
"Ticket": {
"Age": "0 h 09 m",
"Article": [
{
"Body": "Testing",
"ContentType": "text/plain; charset=utf8",
"CreateTime": "2020-04-26 11:05:07",
"From": "\"Jens Bothe\" <jens.bothe@otrs.com\>",
"ID": "11187",
"Subject": "TestArticle"
},
{
"Body": "ClosingBody",
"ContentType": "text/plain; charset=utf8",
"CreateTime": "2020-04-26 11:05:12",
"From": "SIEM Webservice",
"ID": "11188",
"Subject": "ClosingSubject"
}
],
"Created": "2020-04-26 11:05:07",
"CustomerID": "jb",
"DynamicField": {
"Firstname": "Jens",
"Gender": "male"
},
"ID": "7023",
"Lock": "unlock",
"Number": "2020042610000031",
"Owner": "siem",
"Priority": "1 very low",
"Queue": "Inbox::SIEM",
"State": "open",
"Title": "UpdatedTitle",
"Type": "Incident"
}
}
}
Human Readable Output
OTRS Ticket 7023
| ID | Number | Age | Title | State | Lock | Queue | Owner | CustomerID | Priority | Type | Created | DynamicField |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 7023 | 2020042610000031 | 0 h 09 m | UpdatedTitle | open | unlock | Inbox::SIEM | siem | jb | 1 very low | Incident | 2020-04-26 11:05:07 | Firstname: Jens Gender: male |
Articles
| ID | From | Subject | Body | CreateTime | ContentType |
|---|---|---|---|---|---|
| 11187 | “Jens Bothe” <jens.bothe@otrs.com> | TestArticle | Testing | 2020-04-26 11:05:07 | text/plain; charset=utf8 |
| 11188 | SIEM Webservice | ClosingSubject | ClosingBody | 2020-04-26 11:05:12 | text/plain; charset=utf8 |
otrs-search-ticket
Search for an OTRS ticket using search filters
Base Command
otrs-search-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| state | Ticket states to filter for in CSV format (for example New, Open) | Optional |
| created_before | Filter for a ticket created before this date. (formatted as <number> <time unit>, for example 1 day, 30 minutes, 2 weeks, 6 months, 1 year) | Optional |
| created_after | Filter for a ticket created after this date. (formatted as <number> <time unit>, for example 1 day, 30 minutes, 2 weeks, 6 months, 1 year) | Optional |
| title | Ticket Title to filter for | Optional |
| queue | Ticket Queues to filter for in CSV format (for example Raw,Misc) | Optional |
| priority | Ticket priority to filter for in CSV format (for example 4High,5VeryHigh). For the use of a custom priority, you can specify a custom value outside the predefined set. | Optional |
| type | Ticket type to filter for | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| OTRS.Ticket.ID | string | Ticket ID |
| OTRS.Ticket.Number | string | Ticket number |
| OTRS.Ticket.Created | date | Ticket creation date |
| OTRS.Ticket.CustomerUser | string | Customer user related to ticket |
| OTRS.Ticket.Owner | string | Ticket owner |
| OTRS.Ticket.Priority | string | Ticket priority |
| OTRS.Ticket.Queue | string | Queue the ticket is in |
| OTRS.Ticket.State | string | Ticket state |
| OTRS.Ticket.Title | string | Ticket title |
| OTRS.Ticket.Type | string | Ticket type |
Command Example
!otrs-search-ticket state="PendingReminder" title="7023"
Context Example
{}
Human Readable Output
No results found
otrs-create-ticket
Create a new ticket in OTRS
Base Command
otrs-create-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| title | Title to assign to the new ticket | Required |
| queue | Queue to place the new ticket in | Required |
| state | State to assign to the new ticket. For the use of a custom state, you can specify a custom value outside the predefined set. | Required |
| priority | Priority to assign to the new ticket. For the use of a custom priority, you can specify a custom value outside the predefined set. | Required |
| customer_user | Customer user related to the new ticket | Required |
| article_subject | Article subject to apply to the new ticket | Required |
| article_body | Text to add to the article body of the new ticket | Required |
| article_content_type | Content Type of the article body. Can be either ‘text/plain; charset=utf8’ or ‘text/html; charset=utf8’. Defaults to ‘text/plain; charset=utf8’. | Optional |
| type | Ticket type to assign to the new ticket | Optional |
| dynamic_fields | Dynamic fields to apply to the new ticket in the format: field1=value1,field2=value2. For example: ProcessManagementProcessID=1,ProcessManagementActivityStatus=2 | Optional |
| attachment | File entry ID of the file to add as an attachment to the new ticket in CSV format. For example: 123@20,124@21 | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| OTRS.Ticket.Article.Subject | string | Ticket article subject |
| OTRS.Ticket.Article.Body | string | Ticket article body |
| OTRS.Ticket.Article.ContentType | string | Content Type of the article body. |
| OTRS.Ticket.ID | string | Ticket ID |
| OTRS.Ticket.Number | string | Ticket number |
| OTRS.Ticket.Created | date | Ticket creation date |
| OTRS.Ticket.Priority | string | Ticket priority |
| OTRS.Ticket.Queue | string | Queue that the ticket is in |
| OTRS.Ticket.State | string | Ticket state |
| OTRS.Ticket.Title | string | Ticket title |
| OTRS.Ticket.Type | string | Ticket type |
| OTRS.Ticket.CustomerUser | string | Customer user related to ticket |
| OTRS.Ticket.DynamicField | string | Ticket dynamic fields |
Command Example
!otrs-create-ticket title="TestTicket" queue="Inbox::SIEM" state="New" priority="2Low" customer_user="jb" article_subject="TestArticle" article_body="Testing" type="Unclassified"
Context Example
{
"OTRS": {
"Ticket": {
"Article": {
"Body": "Testing",
"Subject": "TestArticle"
},
"CustomerUser": "jb",
"DynamicField": [],
"ID": "7024",
"Number": "2020042610000049",
"Priority": "2 low",
"Queue": "Inbox::SIEM",
"State": "new",
"Title": "TestTicket",
"Type": "Unclassified"
}
}
}
Human Readable Output
Created ticket 7024 successfully
otrs-update-ticket
Update an OTRS ticket
Base Command
otrs-update-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | Ticket ID of the ticket to update | Required |
| title | Ticket title of the ticket to update | Optional |
| state | Ticket state of the ticket to update. For the use of a custom state, you can specify a custom value outside the predefined set. | Optional |
| priority | Priority of the ticket to update. For the use of a custom priority, you can specify a custom value outside the predefined set. | Optional |
| article_subject | Article subject of the ticket to update | Optional |
| article_body | Article body of the ticket to update | Optional |
| article_content_type | Content Type of the article body. Can be either ‘text/plain; charset=utf8’ or ‘text/html; charset=utf8’. Defaults to ‘text/plain; charset=utf8’. | Optional |
| queue | Queue that the ticket to update is in | Optional |
| type | Ticket type of the ticket to update | Optional |
| dynamic_fields | Dynamic fields to apply to the updated ticket, in the format: field1=value1,field2=value2. For example: ProcessManagementProcessID=1,ProcessManagementActivityStatus=2 | Optional |
| attachment | File entry ID of the file to add as an attachment to the updated ticket in CSV format. For example: 123@20,124@21 | Optional |
| lock | Wether to change the lock state of the ticket. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| OTRS.Ticket.Article.Subject | string | Ticket article subject |
| OTRS.Ticket.Article.Body | string | Ticket article body |
| OTRS.Ticket.Article.ContentType | string | Content Type of the article body. |
| OTRS.Ticket.ID | string | Ticket ID |
| OTRS.Ticket.Created | date | Ticket creation date |
| OTRS.Ticket.Priority | string | Ticket priority |
| OTRS.Ticket.Queue | string | Queue that the ticket is in |
| OTRS.Ticket.State | string | Ticket state |
| OTRS.Ticket.Title | string | Ticket title |
| OTRS.Ticket.Type | string | Ticket type |
Command Example
!otrs-update-ticket ticket_id="7023" title="UpdatedTitle" state="Open" priority="1VeryLow" type="Incident"
Context Example
{
"OTRS": {
"Ticket": {
"ID": "7023",
"Priority": "1 very low",
"State": "open",
"Title": "UpdatedTitle",
"Type": "Incident"
}
}
}
Human Readable Output
Updated ticket 7023 successfully
otrs-close-ticket
Close an OTRS ticket
Base Command
otrs-close-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | Ticket ID of the ticket to close | Required |
| article_subject | Article subject of the ticket to close | Required |
| article_body | Article body of the ticket to close | Required |
| article_content_type | Content Type of the article body. Can be either ‘text/plain; charset=utf8’ or ‘text/html; charset=utf8’. Defaults to ‘text/plain; charset=utf8’. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| OTRS.Ticket.ID | string | Ticket ID |
| OTRS.Ticket.State | string | Ticket state |
| OTRS.Ticket.Article.Subject | string | Ticket article subject |
| OTRS.Ticket.Article.Body | string | Ticket article body |
| OTRS.Ticket.Article.ContentType | string | Content Type of the article body. |
Command Example
!otrs-close-ticket ticket_id="7023" article_subject="ClosingSubject" article_body="ClosingBody"
Context Example
{
"OTRS": {
"Ticket": {
"Article": {
"Body": "ClosingBody",
"Subject": "ClosingSubject"
},
"ID": "7023",
"State": "closed successful"
}
}
}
Human Readable Output
Closed ticket 7023 successfully
Configuration parameters
server— OTRS Server URL (e.g. http://example.com) (required)credentials— OTRS Credentials (required)unsecure— Trust any certificate (not secure)proxy— Use system proxy settingsisFetch— Fetch incidentsincidentType— Incident typefetch_queue— Queues to fetch tickets from ("Any" fetches from all queues. CSV supported, e.g., Misc,Raw)fetch_priority— Fetch tickets in priorityfetch_time— First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)look_back— Days to look back when fetchinguse_legacy_sessions— Use legacy sessionsincidentFetchInterval— Incidents Fetch Intervaltag— Entry Tag To OTRStag_from_otrs— Entry Tag From OTRSmirror_direction— Incident Mirroring Directionwebservice_config_ticket— Web Service Config for Tickets
Commands (5)
-
otrs-close-ticketClose an OTRS ticket.
-
otrs-create-ticketCreate a new ticket in OTRS.
-
otrs-get-ticketRetrieves details for an OTRS ticket by ticket ID or ticket number. The arguments are both optional, but at least one is required for this integration to run.
-
otrs-search-ticketSearch for an OTRS ticket using search filters.
-
otrs-update-ticketUpdate an OTRS ticket.
from datetime import datetime import json import demistomock as demisto import OTRS import pytest OTRS_TICKET_MIRROR = { "Age": 238078, "ArchiveFlag": "n", "Article": [ { "ArticleID": 9999, "ArticleNumber": 1, "ArticlePlain": None, "Bcc": "", "Body": "test123", "Cc": "", "ChangedBy": 18, "ChangeTime": "2023-09-26 11:33:28", "Charset": "utf8", "CommunicationsChannelID": 3, "ContentType": "text/plain; charset=utf8", "ContentCharset": "utf8", "CreateBy": 18, "CreateTime": "2023-09-26 11:33:28", "From": "demistobot", "InReplyTo": "", "IncomingTime": 1695720808, "IsVisibleForCustomer": 1, "MessageID": "", "MimeType": "text/plain", "References": "", "ReplyTo": "", "SenderType": "agent", "SenderTypeID": "1", "Subject": "test", "TicketID": 1234, "TimeUnit": 0, "To": "IncidentResponse", } ], "ChangedBy": 1, "Changed": "2023-09-26 11:33:29", "CreatedBy": 18, "CustomerID": "", "CustomerUserID": "test@mail.com", "DynamicField": [{"Name": "IncidentDescription", "Value": None}, {"Name": "TLP", "Value": None}], "EscalationResponseTime": 0, "EscalationSolutionTime": 0, "EscalationTime": 0, "EscalationUpdateTime": 0, "GroupID": 6, "Lock": "unlock", "LockID": 1, "Owner": "demistobot", "OwnerID": 22, "Priority": "Severity normal", "PriorityBackgroundColor": "#cdcdcd", "PriorityForgroundColor": "#ffffff", "PriorityID": 3, "Queue": "Incident Response", "QueueID": 12, "RealTillTimeNotUsed": 0, "Responsible": "root@localhost", "ResponsibleID": 1, "SLAID": "", "ServiceID": "", "State": "new", "StateID": 1, "StateType": "new", "TicketID": 1234, "TicketNumber": "1911325", "TimeUnit": 0, "Title": "Demisto Test", "Type": "Unclassified", "TypeID": 1, "UnlockTimeout": 1695720808, "UntilTime": 0, } def load_json(path): with open(path, encoding="utf-8") as f: return json.load(f) @pytest.mark.parametrize( argnames="queue, expected_time_arg", argvalues=[ ("Any", "2000-01-02 00:00:01"), ("queue_1,queue_2", "2000-01-01 00:00:01"), ], ) def test_correct_time_in_fetch_incidents_(mocker, queue, expected_time_arg): """ Given - fetch incident when queue is specified in params When - run the fetch incident command Then - assert the created_after arg in search_ticket are as expected day before the last_run if queue is specified and equal to last_run if not specified """ mocker.patch.object(demisto, "getLastRun", return_value={"time": "2000-01-02 00:00:00", "last_fetched_ids": []}) mocker.patch.object(demisto, "params", return_value={}) mocker.patch.object(demisto, "getIntegrationContext", return_value={"SessionID": "1234"}) mocker.patch.object(OTRS.OTRSClient, "search_ticket", return_value=[]) mocker.patch.object(OTRS, "parse_date_range", return_value=(datetime.strptime("2020-10-10", "%Y-%m-%d"), None)) mocker.patch.object(demisto, "setLastRun") otrs_client = OTRS.OTRSClient("base_url", "username", "password", https_verify=False, use_legacy_sessions=False) # run OTRS.fetch_incidents(otrs_client, queue, None, "3 days", 1) # validate created_after = otrs_client.search_ticket.call_args[1]["created_after"] assert expected_time_arg == datetime.strftime(created_after, "%Y-%m-%d %H:%M:%S") @pytest.mark.parametrize( argnames="last_run_obj, expected_last_run", argvalues=[ ({}, {"time": "2020-10-10 00:00:00", "last_fetched_ids": []}), ({"time": "2000-01-01 00:00:00", "last_fetched_ids": ["1"]}, {"time": "2000-01-01 00:00:01", "last_fetched_ids": []}), ], ) @pytest.mark.parametrize( argnames="queue, expected_queue_arg", argvalues=[ ("Any", None), ("queue_1,queue_2", ["queue_1", "queue_2"]), ], ) def test_fetch_incidents__queue_specified(mocker, last_run_obj, expected_last_run, queue, expected_queue_arg): """ Given - fetch incident when queue is specified in params When - run the fetch incident command Then - assert the created_after arg in search_ticket are as expected assert the last run was as expected """ # mocker.patch.object(OTRS, 'FETCH_QUEUE', queue) mocker.patch.object(demisto, "getLastRun", return_value=last_run_obj) mocker.patch.object(demisto, "params", return_value={}) mocker.patch.object(demisto, "getIntegrationContext", return_value={"SessionID": "1234"}) mocker.patch.object(OTRS.OTRSClient, "search_ticket", return_value=[]) mocker.patch.object(OTRS, "parse_date_range", return_value=(datetime.strptime("2020-10-10", "%Y-%m-%d"), None)) mocker.patch.object(demisto, "setLastRun") otrs_client = OTRS.OTRSClient("base_url", "username", "password", https_verify=False, use_legacy_sessions=False) # run OTRS.fetch_incidents(otrs_client, queue, None, "3 days", 1) # validate demisto.setLastRun.assert_called_with(expected_last_run) assert expected_queue_arg == otrs_client.search_ticket.call_args[1]["queue"] def test_get_remote_data(mocker): """ Given: - arguments: id and LastUpdate(set to lower then the modification time). - OTRS ticket When - running get_remote_data_command. Then - The ticket was updated with the entries. """ args = {"id": "1234", "lastUpdate": 0} mocker.patch.object(demisto, "getIntegrationContext", return_value={"SessionID": "1234"}) mocker.patch.object(OTRS.OTRSClient, "get_ticket", return_value=OTRS_TICKET_MIRROR) otrs_client = OTRS.OTRSClient("base_url", "username", "password", https_verify=False, use_legacy_sessions=False) res = OTRS.get_remote_data_command(otrs_client, args) assert res.__dict__["entries"][0]["Tags"] == ["FromOTRS"] assert ( res.__dict__["entries"][0]["Contents"] == """### OTRS Mirroring Update |ArticleID|To|Subject|CreateTime|From|ContentType|Body| |---|---|---|---|---|---|---| | 9999 | IncidentResponse | test | 2023-09-26 11:33:28 | demistobot | text/plain; charset=utf8 | test123 | """ ) def test_update_ticket_command(mocker): """ Given: - OTRS ticket - arguments to change in the ticket When: - running update_ticket_command Then: - All arguments are part of the call to the actual API - The CommandResults contains the expected data for the user """ from OTRS import update_ticket_command update_ticket_data = load_json("./test_data/test_update_ticket_command_data.json") args = update_ticket_data.get("command_args") update_ticket_response = update_ticket_data.get("response") expected_command_results = update_ticket_data.get("expected_command_results") mocker.patch.object(demisto, "getIntegrationContext", return_value={"SessionID": "1234"}) mocker.patch.object(OTRS.OTRSClient, "execute_otrs_method", return_value=update_ticket_response) otrs_client = OTRS.OTRSClient("base_url", "username", "password", https_verify=False, use_legacy_sessions=False) command_results = update_ticket_command(otrs_client, args) assert command_results.to_context() == expected_command_results def test_calculate_age(): """ Given: An integer representing seconds When - Running otrs-get-ticket and calculating the age of the ticket. Then - The correct age is returned. """ from OTRS import calculate_age result = calculate_age(6000) assert result == "1 h 40 m"