AzureDataExplorer
Use the Azure Data Explorer integration to collect and analyze data inside Azure Data Explorer clusters, and to manage search queries.
Analytics & SIEM · Azure Data Explorer
Details
| ID | AzureDataExplorer |
|---|---|
| Provider | Microsoft |
| Category | Analytics & SIEM |
| From Version | 6.0.0 |
| Docker Image | demisto/auth-utils:1.0.0.10133006 |
| Supported Modules | Agentix XSIAM |
README
Use the Azure Data Explorer integration to collect and analyze data inside Azure Data Explorer clusters, and to manage search queries.
This integration was integrated and tested with version V1 of AzureDataExplorer.
Authorization
In order to connect to the Azure Data Explorer using either Cortex XSOAR Azure App or the Self-Deployed Azure App, use one of the following methods:
- Authorization Code Flow (Recommended).
- Device Code Flow.
- Client Credentials Flow.
Self-Deployed Azure App
To use a self-configured Azure application, you need to add a new Azure App Registration in the Azure Portal.
To add the registration, refer to the following Microsoft article steps 1-8.
Required permissions
- Azure Data Explorer - permission
user_impersonationof type Delegated. - Microsoft Graph - permission
offline_accessof type Delegated.
To add a permission:
- Navigate to Azure Poral > Home > App registrations.
- Search for your app under ‘all applications’.
- Click API permissions > Add permission.
- Search for the specific Microsoft API and select the specific permission of type Delegated.
Authentication Using the Authorization Code Flow (recommended)
- In the Authentication Type field, select the Authorization Code option.
- In the Application ID field, enter your Client/Application ID.
- In the Client Secret field, enter your Client Secret.
- In the Tenant ID field, enter your Tenant ID .
- In the Application redirect URI field, enter your Application redirect URI.
- Save the instance.
- Run the
!azure-data-explorer-generate-login-urlcommand in the War Room and follow the instruction. - Save the instance.
Authentication Using the Device Code Flow
Use the device code flow
to link Azure Data Explorer with Cortex XSOAR.
- Fill in the required parameters.
- In the Authentication Type field, select the Device Code option.
- Run the !azure-data-explorer-auth-start command.
- Follow the instructions that appear.
- Run the !azure-data-explorer-auth-complete command.
At end of the process you’ll see a message that you’ve logged in successfully.
Cortex XSOAR Azure App
In order to use the Cortex XSOAR Azure application, use the default application ID (a9ce8db2-847a-46af-9bfb-725d8a8d3c53).
Authentication Using the Client Credentials Flow
-
Assign Azure roles using the Azure portal Microsoft article
Note: In the Select members section, assign the application you created earlier.
-
To configure a Microsoft integration that uses this authorization flow with a self-deployed Azure application:
a. In the Authentication Type field, select the Client Credentials option.
b. In the Application ID field, enter your Client/Application ID.
e. In the Tenant ID field, enter your Tenant ID .
f. In the Client Secret field, enter your Client Secret.
g. Click Test to validate the URLs, token, and connection
h. Save the instance.
Configure Azure Data Explorer in Cortex
| Parameter | Description | Required |
|---|---|---|
| Cluster URL (e.g. https://help.kusto.windows.net) | True | |
| Application ID | True | |
| Client Activity Prefix | A customized prefix of the client activity identifier for the query execution. For example, for a prefix value of ‘XSOAR-DataExplorer’, the client activity ID will be in the format of: ‘XSOAR-DataExplorer;<UUID>’. | True |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False | |
| Authentication Type | Type of authentication - could be Authorization Code Flow (recommended), Device Code Flow or Client Credentials Flow. | False |
| Tenant ID | For Authorization Code or Client Credentials Flows. | False |
| Client Secret | For Authorization Code or Client Credentials Flows. | False |
| Application redirect URI (for Authorization Code mode) | False | |
| Authorization code | for Authorization Code mode - received from the authorization step. see Detailed Instructions (?) section | 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.
azure-data-explorer-search-query-execute
Execute a Kusto Query Language (KQL) query against the given database inside a cluster. The Kusto query is a read-only request to process data and return results. To learn more about KQL go to https://docs.microsoft.com/en-us/azure/kusto/query/.
Base Command
azure-data-explorer-search-query-execute
Input
| Argument Name | Description | Required |
|---|---|---|
| query | Kusto Query Language (KQL) search query to execute on given database. | Required |
| database_name | The name of the database to execute the query on. | Required |
| timeout | The timeout for the execution of the search query on the server side. The timeout is a float number in minutes that ranges from 0 to 60. Default is 5. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| AzureDataExplorer.SearchQueryResults.Query | String | The executed query on the given database. |
| AzureDataExplorer.SearchQueryResults.ClientActivityID | String | The Client Activity ID. A unique identifier of the executed query. |
| AzureDataExplorer.SearchQueryResults.PrimaryResults | Unknown | The results of the query execution. |
| AzureDataExplorer.SearchQueryResults.Database | String | The database against which the query will be executed. |
Command Example
!azure-data-explorer-search-query-execute database_name=Samples query="StormEvents| limit 1"
Context Example
{
"AzureDataExplorer": {
"SearchQueryResults": {
"ClientActivityID": "XSOAR-DataExplorer;759d43a9-cdc1-4882-8f8b-3e8d8a703f1e",
"Database": "Samples",
"PrimaryResults": [
{
"BeginLat": 28.0393,
"BeginLocation": "MELBOURNE BEACH",
"BeginLon": -80.6048,
"DamageCrops": 0,
"DamageProperty": 0,
"DeathsDirect": 0,
"DeathsIndirect": 0,
"EndLat": 28.0393,
"EndLocation": "MELBOURNE BEACH",
"EndLon": -80.6048,
"EndTime": "2007-09-29T08:11:00",
"EpisodeId": 11091,
"EpisodeNarrative": "Showers and thunderstorms lingering along the coast produced waterspouts in Brevard County.",
"EventId": 61032,
"EventNarrative": "A waterspout formed in the Atlantic southeast of Melbourne Beach and briefly moved toward shore.",
"EventType": "Waterspout",
"InjuriesDirect": 0,
"InjuriesIndirect": 0,
"Source": "Trained Spotter",
"StartTime": "2007-09-29T08:11:00",
"State": "ATLANTIC SOUTH",
"StormSummary": {
"Details": {
"Description": "A waterspout formed in the Atlantic southeast of Melbourne Beach and briefly moved toward shore.",
"Location": "ATLANTIC SOUTH"
},
"EndTime": "2007-09-29T08:11:00.0000000Z",
"StartTime": "2007-09-29T08:11:00.0000000Z",
"TotalDamages": 0
}
}
],
"Query": "StormEvents| limit 1"
}
}
}
Human Readable Output
Results of executing search query with client activity ID: XSOAR-DataExplorer;759d43a9-cdc1-4882-8f8b-3e8d8a703f1e
Begin Lat Begin Location Begin Lon Damage Crops Damage Property Deaths Direct Deaths Indirect End Lat End Location End Lon End Time Episode Id Episode Narrative Event Id Event Narrative Event Type Injuries Direct Injuries Indirect Source Start Time State Storm Summary 28.0393 MELBOURNE BEACH -80.6048 0 0 0 0 28.0393 MELBOURNE BEACH -80.6048 2007-09-29T08:11:00 11091 Showers and thunderstorms lingering along the coast produced waterspouts in Brevard County. 61032 A waterspout formed in the Atlantic southeast of Melbourne Beach and briefly moved toward shore. Waterspout 0 0 Trained Spotter 2007-09-29T08:11:00 ATLANTIC SOUTH TotalDamages: 0
StartTime: 2007-09-29T08:11:00.0000000Z
EndTime: 2007-09-29T08:11:00.0000000Z
Details: {“Description”: “A waterspout formed in the Atlantic southeast of Melbourne Beach and briefly moved toward shore.”, “Location”: “ATLANTIC SOUTH”}
azure-data-explorer-search-query-list
List search queries that have reached a final state in the given database. A database admin or database monitor can see any command that was invoked on their database. Other users can only see queries that they themselves invoked.
Base Command
azure-data-explorer-search-query-list
Input
| Argument Name | Description | Required |
|---|---|---|
| database_name | The name of the database from which to list the completed search queries. . | Required |
| client_activity_id | The client activity ID property of the search query. Use this value to get a specific search query. | Optional |
| limit | The maximum number of completed queries to return. Default is 50. | Optional |
| page | The page number from which to start a search. Default is 1. | Optional |
| page_size | The maximum number of completed queries to return per page. If this argument is not provided, an automatic pagination will be made according to the limit argument. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| AzureDataExplorer.SearchQuery.ClientActivityId | String | The client activity ID. A unique identifier of the query execution. |
| AzureDataExplorer.SearchQuery.Text | String | The search query text. |
| AzureDataExplorer.SearchQuery.Database | String | The name of the database that the search query is run on. |
| AzureDataExplorer.SearchQuery.StartedOn | Date | The query execution start time in UTC. |
| AzureDataExplorer.SearchQuery.LastUpdatedOn | Date | The last update time of the query. |
| AzureDataExplorer.SearchQuery.Duration | Date | The search query runtime. |
| AzureDataExplorer.SearchQuery.State | String | The search query state. |
| AzureDataExplorer.SearchQuery.RootActivityId | String | The root activity ID. |
| AzureDataExplorer.SearchQuery.User | String | The user who performed the query. |
| AzureDataExplorer.SearchQuery.FailureReason | String | The reason for query failure. |
| AzureDataExplorer.SearchQuery.TotalCpu | String | The total CPU clock time (User mode + Kernel mode) consumed by this query. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Memory.Hits | Number | The number of cache hits. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Memory.Misses | Number | The number of cache misses. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Disk.Hits | Number | The number of disk hits. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Disk.Misses | Number | The number of disk misses. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Shards.Hot.HitBytes | Number | The amount of data (in bytes) which was found in the hot data cache of the table’s extents, during the search query execution. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Shards.Hot.MissBytes | Number | The amount of data (in bytes) which was not found in the hot data cache of the table’s extents, during the search query execution. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Shards.Hot.RetrieveBytes | Number | The amount of data (in bytes) that was retrieved from hot data cache of the table’s extents, during the search query execution. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Shards.Cold.HitBytes | Number | The amount of data (in bytes) which was found in the cold data cache of the table’s extents, during the search query execution. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Shards.Cold.MissBytes | Number | The amount of data (in bytes) which was not found in the cold data cache of the table’s extents, during the search query execution. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Shards.Cold.RetrieveBytes | Number | The amount of data (in bytes) that was retrieved from cold data cache during the search query execution. |
| AzureDataExplorer.SearchQuery.CacheStatistics.Shards.BypassBytes | Number | The amount of data (in bytes) that was bypassed (reloaded) in the cache of the table’s extents during the search query execution. |
| AzureDataExplorer.SearchQuery.Application | String | The application name that invoked the command. |
| AzureDataExplorer.SearchQuery.MemoryPeak | Number | The peak memory usage of the query execution. |
| AzureDataExplorer.SearchQuery.ScannedExtentsStatistics.MinDataScannedTime | Date | The minimum data scan time. |
| AzureDataExplorer.SearchQuery.ScannedExtentsStatistics.MaxDataScannedTime | Date | The maximum data scan time. |
| AzureDataExplorer.SearchQuery.ScannedExtentsStatistics.TotalExtentsCount | Number | The total number of extents which were used during the query execution. |
| AzureDataExplorer.SearchQuery.ScannedExtentsStatistics.ScannedExtentsCount | Number | The number of extents which were scanned during the query execution. |
| AzureDataExplorer.SearchQuery.ScannedExtentsStatistics.TotalRowsCount | Number | The total row count of extents which were used during the query execution. |
| AzureDataExplorer.SearchQuery.ScannedExtentsStatistics.ScannedRowsCount | Number | The number of scanned rows of an extent during query execution. |
| AzureDataExplorer.SearchQuery.Principal | String | The principal that invoked the query. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.SecurityTokenPresent | Boolean | Whether the security token is present in the request or not. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.AuthorizationScheme | String | The authorization scheme. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.RequestHostName | String | The hostname of the request. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.LocalClusterName | String | The cluster name. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.OriginClusterName | String | The origin cluster name. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.api_version | String | The API version. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.request_readonly | Boolean | Whether the request is read-only or not. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.servertimeout | Number | The server timeout value. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.servertimeoutorigin | String | The server timeout origin. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.query_datascope | Number | The query datascope. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.query_fanout_nodes_percent | Number | The percentage of the query nodes in the cluster to use per subquery distribution operation. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.query_fanout_threads_percent | Number | The percentage of CPUs the cluster will assign on each node. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.maxmemoryconsumptionperiterator | Number | The maximum amount of memory that a single query plan result set iterator can hold. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.max_memory_consumption_per_query_per_node | Number | The maximum amount of memory that can be used on a single node for a specific query. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.truncationmaxsize | Number | The maximum overall data size returned by the query, in bytes. |
| AzureDataExplorer.SearchQuery.ClientRequestProperties.Options.truncationmaxrecords | Number | The maximum number of records returned by the query. |
| AzureDataExplorer.SearchQuery.ResultSetStatistics.TableCount | Number | The number of tables that were retrieved following search query execution. |
| AzureDataExplorer.SearchQuery.ResultSetStatistics.TablesStatistics.RowCount | Number | The row count of the table retrieved following search query execution. |
| AzureDataExplorer.SearchQuery.ResultSetStatistics.TablesStatistics.TableSize | Number | The total size in bytes of the table retrieved following search query execution. |
| AzureDataExplorer.SearchQuery.WorkloadGroup | String | The workload group which the query was assigned to. The query is executed using the policies assigned to the workload group. There are two pre-defined workload groups (internal and default) and up to 10 custom workload groups which may be defined at the cluster level. |
Command Example
!azure-data-explorer-search-query-list database_name=Samples limit=1
Context Example
{
"AzureDataExplorer": {
"SearchQuery": {
"Application": "KusWeb",
"CacheStatistics": {
"Disk": {
"Hits": 0,
"Misses": 0
},
"Memory": {
"Hits": 0,
"Misses": 0
},
"Shards": {
"BypassBytes": 0,
"Cold": {
"HitBytes": 0,
"MissBytes": 0,
"RetrieveBytes": 0
},
"Hot": {
"HitBytes": 0,
"MissBytes": 0,
"RetrieveBytes": 0
}
}
},
"ClientActivityId": "KustoWebV2;f1be2c7e-f810-437b-a1f8-f8bbbedf238d",
"ClientRequestProperties": {
"AuthorizationScheme": "Bearer",
"LocalClusterName": "https://help.kusto.windows.net/",
"Options": {
"api_version": "v2",
"max_memory_consumption_per_query_per_node": 2000000000,
"maxmemoryconsumptionperiterator": 5368709120,
"query_datascope": 1,
"query_fanout_nodes_percent": 100,
"query_fanout_threads_percent": 100,
"query_language": "csl",
"queryconsistency": "strongconsistency",
"request_app_name": "KusWeb",
"request_readonly": true,
"request_readonly_hardline": false,
"servertimeout": 600000000,
"truncationmaxrecords": 500000,
"truncationmaxsize": 67108864
},
"OriginClusterName": "https://help.kusto.windows.net/",
"RequestHostName": "https://help.kusto.windows.net:443/",
"SecurityTokenPresent": true
},
"Database": "Samples",
"Duration": "0:00:00",
"FailureReason": "[none]",
"LastUpdatedOn": "2021-11-24T15:15:27",
"MemoryPeak": 0,
"Principal": "aaduser=xxx-xxxx-xxxx",
"ResultSetStatistics": {
"TableCount": 2,
"TablesStatistics": [
{
"RowCount": 0,
"TableSize": 0
},
{
"RowCount": 2,
"TableSize": 1244
}
]
},
"RootActivityId": "2b9e0ec8-f6b0-407e-90b6-68eba3777564",
"ScannedExtentsStatistics": {
"MaxDataScannedTime": null,
"MinDataScannedTime": null,
"ScannedExtentsCount": 0,
"ScannedRowsCount": 0,
"TotalExtentsCount": 0,
"TotalRowsCount": 0
},
"StartedOn": "2021-11-24T15:15:27",
"State": "Completed",
"Text": "set notruncation;\nCovid19",
"TotalCpu": "0:00:00",
"User": "dataExplorer@qmasterslabgmail.onmicrosoft.com",
"WorkloadGroup": "default"
}
}
}
Human Readable Output
List of Completed Search Queries
Showing 0 to 1 records out of 134.
Client Activity Id User Text Database Started On Last Updated On State KustoWebV2;f1be2c7e-f810-437b-a1f8-f8bbbedf238d dataExplorer@qmasterslabgmail.onmicrosoft.com set notruncation;
Covid19Samples 2021-11-24T15:15:27 2021-11-24T15:15:27 Completed
azure-data-explorer-running-search-query-list
List currently executing search queries in the given database. A database admin or database monitor can see any search query that was invoked on their database.
Other users can only see search queries that they themselves invoked.
Base Command
azure-data-explorer-running-search-query-list
Input
| Argument Name | Description | Required |
|---|---|---|
| database_name | The database name. | Required |
| client_activity_id | The client activity ID property of the search query. Use this to get a specific running search query. | Optional |
| limit | The maximum number of running queries to return. Default is 50. | Optional |
| page | The page number from which to start a search. Default is 1. | Optional |
| page_size | The maximum number of running queries to return per page. If this argument is not provided, an automatic pagination will be made according to the limit argument. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| AzureDataExplorer.RunningSearchQuery.ClientActivityId | String | The client activity ID. A unique identifier of the query execution. |
| AzureDataExplorer.RunningSearchQuery.Text | String | The search query text. |
| AzureDataExplorer.RunningSearchQuery.Database | String | The name of the database that the search query is run on. |
| AzureDataExplorer.RunningSearchQuery.StartedOn | Date | The query execution start time in UTC. |
| AzureDataExplorer.RunningSearchQuery.LastUpdatedOn | String | The last update time of the query. |
| AzureDataExplorer.RunningSearchQuery.Duration | Date | The search query runtime duration. |
| AzureDataExplorer.RunningSearchQuery.State | String | The search query state. |
| AzureDataExplorer.RunningSearchQuery.RootActivityId | String | The root activity ID. |
| AzureDataExplorer.RunningSearchQuery.User | String | The user who performed the query. |
| AzureDataExplorer.RunningSearchQuery.FailureReason | String | The reason for query failure. |
| AzureDataExplorer.RunningSearchQuery.TotalCpu | String | The total CPU clock time (User mode + Kernel mode) consumed by this query. |
| AzureDataExplorer.RunningSearchQuery.CacheStatistics | Unknown | The cache statistics. |
| AzureDataExplorer.RunningSearchQuery.Application | String | The application name that invoked the command. |
| AzureDataExplorer.RunningSearchQuery.MemoryPeak | Number | The peak memory usage of the running query execution. |
| AzureDataExplorer.RunningSearchQuery.ScannedExtentsStatistics | Unknown | The scanned extent count. |
| AzureDataExplorer.RunningSearchQuery.Principal | String | The principal that invoked the query. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.SecurityTokenPresent | Boolean | Whether the security token is present in the request or not. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.AuthorizationScheme | String | The authorization scheme. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.RequestHostName | String | The hostname of the request. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.LocalClusterName | String | The cluster name. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.OriginClusterName | String | The origin cluster name. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.api_version | String | The API version. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.request_readonly | Boolean | Whether the request is read-only or not. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.servertimeout | Number | The server timeout value. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.servertimeoutorigin | String | The server timeout origin. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.query_datascope | Number | The query datascope. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.query_fanout_nodes_percent | Number | The percentage of the query nodes in the cluster to use per subquery distribution operation. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.query_fanout_threads_percent | Number | The percentage of CPUs the cluster will assign on each node. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.maxmemoryconsumptionperiterator | Number | The maximum amount of memory that a single query plan result set iterator can hold. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.max_memory_consumption_per_query_per_node | Number | The maximum amount of memory that can be used on a single node for a specific query. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.truncationmaxsize | Number | The maximum overall data size returned by the query, in bytes. |
| AzureDataExplorer.RunningSearchQuery.ClientRequestProperties.Options.truncationmaxrecords | Number | The maximum number of records returned by the query. |
| AzureDataExplorer.RunningSearchQuery.ResultSetStatistics | Unknown | The result set statistics. |
| AzureDataExplorer.RunningSearchQuery.WorkloadGroup | String | The workload group. |
Command Example
!azure-data-explorer-running-search-query-list database_name=Samples limit=1
Context Example
{
"AzureDataExplorer": {
"RunningSearchQuery": {
"Application": "KusWeb",
"CacheStatistics": "null",
"ClientActivityId": "KustoWebV2;c6ff3e99-d2cb-4a3e-ab05-955ae383a7c6",
"ClientRequestProperties": "{\"SecurityTokenPresent\":true,\"AuthorizationScheme\":\"Bearer\",\"RequestHostName\":\"https://help.kusto.windows.net:443/\",\"LocalClusterName\":\"https://help.kusto.windows.net/\",\"OriginClusterName\":\"https://help.kusto.windows.net/\",\"Options\":{\"servertimeout\":600000000,\"queryconsistency\":\"strongconsistency\",\"query_language\":\"csl\",\"request_readonly\":true,\"request_readonly_hardline\":false,\"api_version\":\"v2\",\"request_app_name\":\"KusWeb\",\"query_datascope\":1,\"query_fanout_nodes_percent\":100,\"query_fanout_threads_percent\":100,\"maxmemoryconsumptionperiterator\":5368709120,\"max_memory_consumption_per_query_per_node\":2000000000,\"truncationmaxsize\":67108864,\"truncationmaxrecords\":500000}}",
"Database": "Samples",
"Duration": "0:00:00",
"FailureReason": "",
"LastUpdatedOn": "2021-11-24T15:16:34",
"MemoryPeak": 0,
"Principal": "aaduser=xxx-xxxx-xxxx",
"ResultSetStatistics": "null",
"RootActivityId": "c8233607-30a9-4cc0-9c54-ec716e5fc246",
"ScannedExtentsStatistics": "null",
"StartedOn": "2021-11-24T15:16:34",
"State": "InProgress",
"Text": "set notruncation;\nCovid19_Bing",
"TotalCpu": "0:00:00",
"User": "dataExplorer@qmasterslabgmail.onmicrosoft.com",
"WorkloadGroup": "default"
}
}
}
Human Readable Output
List of Currently running Search Queries
Showing 0 to 1 records out of 2.
Client Activity Id User Text Database Started On Last Updated On State KustoWebV2;c6ff3e99-d2cb-4a3e-ab05-955ae383a7c6 dataExplorer@qmasterslabgmail.onmicrosoft.com set notruncation;
Covid19_BingSamples 2021-11-24T15:16:34 2021-11-24T15:16:34 InProgress
azure-data-explorer-running-search-query-cancel
Starts a best-effort attempt to cancel a specific running search query in the specified database.
Base Command
azure-data-explorer-running-search-query-cancel
Input
| Argument Name | Description | Required |
|---|---|---|
| client_activity_id | The client activity ID of the query to delete. | Required |
| database_name | The database name. | Required |
| reason | The reason for canceling the running query. . | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| AzureDataExplorer.CanceledSearchQuery.RunningQueryCanceled | Boolean | Whether the query was successfully canceled or not. |
| AzureDataExplorer.CanceledSearchQuery.ClientRequestId | String | The client activity ID of the cancelled query. |
| AzureDataExplorer.CanceledSearchQuery.ReasonPhrase | String | The reason for canceling the running query. |
Command Example
!azure-data-explorer-running-search-query-cancel database_name=Samples client_activity_id=xxxx-xxxxx-xxxxx
Context Example
{
"AzureDataExplorer": {
"CanceledSearchQuery": {
"ClientRequestId": "xxxx-xxxxx-xxxxx",
"ReasonPhrase": "None",
"RunningQueryCanceled": false
}
}
}
Human Readable Output
Canceled Search Query xxxx-xxxxx-xxxxx
Client Request Id Reason Phrase Running Query Canceled xxxx-xxxxx-xxxxx None false
azure-data-explorer-auth-start
Run this command to start the authorization process and follow the instructions in the command results.
Base Command
azure-data-explorer-auth-start
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
azure-data-explorer-auth-complete
Run this command to complete the authorization process. This should be used after running the azure-data-explorer-auth-start command.
Base Command
azure-data-explorer-auth-complete
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
azure-data-explorer-auth-reset
Run this command if for some reason you need to rerun the authentication process.
Base Command
azure-data-explorer-auth-reset
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
azure-data-explorer-auth-test
Run this command to test the connectivity to Azure Data Explorer.
Base Command
azure-data-explorer-auth-test
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
azure-data-explorer-generate-login-url
Generate the login url used for Authorization code flow.
Base Command
azure-data-explorer-generate-login-url
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
Command Example
azure-data-explorer-generate-login-url
Human Readable Output
Authorization instructions
- Click on the login URL to sign in and grant Cortex XSOAR permissions for your Azure Service Management.
You will be automatically redirected to a link with the following structure:
REDIRECT_URI?code=AUTH_CODE&session_state=SESSION_STATE- Copy the
AUTH_CODE(without thecode=prefix, and thesession_stateparameter)
and paste it in your instance configuration under the Authorization code parameter.
Configuration parameters
cluster_url— Cluster URL (e.g. https://help.kusto.windows.net) (required)client_id— Application ID (required)client_activity_prefix— Client Activity Prefix (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settingsauthentication_type— Authentication Type (required)tenant_id— Tenant IDcredentials— Client Secretredirect_uri— Application redirect URIauth_code— Authorization code
Commands (9)
-
azure-data-explorer-auth-completeRun this command to complete the authorization process. This should be used after running the azure-data-explorer-auth-start command.
-
azure-data-explorer-auth-resetRun this command if for some reason you need to rerun the authentication process.
-
azure-data-explorer-auth-startRun this command to start the authorization process and follow the instructions in the command results.
-
azure-data-explorer-auth-testRun this command to test the connectivity to Azure Data Explorer.
-
azure-data-explorer-generate-login-urlGenerate the login url used for Authorization code flow.
-
azure-data-explorer-running-search-query-cancelStarts a best-effort attempt to cancel a specific running search query in the specified database.
-
azure-data-explorer-running-search-query-listList currently executing search queries in the given database. A database admin or database monitor can see any search query that was invoked on their database. Other users can only see search queries that they themselves invoked.
-
azure-data-explorer-search-query-executeExecute a Kusto Query Language (KQL) query against the given database inside a cluster. The Kusto query is a read-only request to process data and return results. To learn more about KQL go to https://docs.microsoft.com/en-us/azure/kusto/query/.
-
azure-data-explorer-search-query-listList search queries that have reached a final state in the given database. A database admin or database monitor can see any command that was invoked on their database. Other users can only see queries that they themselves invoked.
from CommonServerPython import * """ IMPORTS """ import uuid from datetime import datetime from decimal import Decimal import requests from azure.kusto.data.response import KustoResponseDataSet, KustoResponseDataSetV1 from MicrosoftApiModule import * # noqa: E402 """ CONSTANTS """ DEFAULT_PAGE_NUMBER = "1" DEFAULT_LIMIT = "50" DATE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S" REQUEST_BASE_TIMEOUT = 20 GRANT_BY_CONNECTION = { "Device Code": DEVICE_CODE, "Authorization Code": AUTHORIZATION_CODE, "Client Credentials": CLIENT_CREDENTIALS, } class DataExplorerClient: """ Azure Data Explorer API Client. """ def __init__( self, cluster_url: str, client_id: str, client_activity_prefix: str, verify: bool, proxy: bool, connection_type: str, tenant_id: str = None, enc_key: str = None, auth_code: str = None, redirect_uri: str = None, ): if "@" in client_id: # for use in test-playbook client_id, refresh_token = client_id.split("@") integration_context = get_integration_context() integration_context.update(current_refresh_token=refresh_token) set_integration_context(integration_context) if not cluster_url.startswith("https://"): raise ValueError("Cluster URL parameter must contain 'https://' as prefix (e.g. https://help.kusto.windows.net).") self.cluster_url = cluster_url self.host = cluster_url.split("https://")[1] self.scope = ( f"{cluster_url}/user_impersonation offline_access user.read" if "Device Code" in connection_type else f"{cluster_url}/.default" ) self.client_activity_prefix = client_activity_prefix client_args = assign_params( self_deployed=True, auth_id=client_id, token_retrieval_url="https://login.microsoftonline.com/organizations/oauth2/v2.0/token" if "Device Code" in connection_type else None, grant_type=GRANT_BY_CONNECTION[connection_type], base_url=cluster_url, verify=verify, proxy=proxy, scope=self.scope, tenant_id=tenant_id, enc_key=enc_key, auth_code=auth_code, redirect_uri=redirect_uri, command_prefix="azure-data-explorer", ) self.ms_client = MicrosoftClient(**client_args) self.connection_type = connection_type def http_request( self, method, url_suffix: str = None, full_url: str = None, params: dict = None, headers=None, data=None, timeout: int = REQUEST_BASE_TIMEOUT, ): if headers is None: headers = {} if data is None: data = {} headers.update( { "Accept": "application/json", "Expect": "100-Continue", "Content-Type": "application/json; charset=utf-8", "Host": self.host, "Connection": "Keep-Alive", } ) res = self.ms_client.http_request( method=method, url_suffix=url_suffix, full_url=full_url, headers=headers, json_data=data, params=params, resp_type="response", timeout=timeout, ok_codes=(200, 204, 400, 401, 403, 404, 409), ) if res.status_code in (200, 204) and not res.text: return res res_json = res.json() if res.status_code in (400, 401, 403, 404, 409): code = res_json.get("error", {}).get("code", "Error") error_msg = res_json.get("error", {}).get("message", res_json) raise ValueError(f"[{code} {res.status_code}] {error_msg}") return res_json def search_query_execute_request( self, database_name: str, query: str, server_timeout: Decimal, client_activity_id: str ) -> dict[str, Any]: """ Execute a KQL query against the given database inside the specified cluster. The query's client activity ID is a combination of the user's client_activity_prefix parameter and a random UUID. Args: database_name (str): The name of the database to execute the query on. query (str): The KQL query to execute against the database. server_timeout: Query execution timeout on server side. client_activity_id (str): A unique ID for query execution. Returns: Dict[str,Any]: API response from Azure. """ data = retrieve_common_request_body(database_name, query, {"Options": {"servertimeout": f"{server_timeout}m"}}) headers = {"x-ms-client-request-id": client_activity_id} response = self.http_request( "POST", url_suffix="/v1/rest/query", data=data, headers=headers, timeout=calculate_total_request_timeout(server_timeout), ) return response def search_queries_list_request(self, database_name: str, client_activity_id: str) -> dict[str, Any]: """ List search queries that have reached a final state on the given database. When the client_activity_id argument is provided, the request will retrieve information regarding specific search query. Args: database_name (str): The name of the database to see the completed queries. client_activity_id (str): client-specified identity of the request. Returns: Dict[str, Any]: API response from Azure. """ mgmt_query = ( f".show queries | where ClientActivityId=='{client_activity_id}'" if client_activity_id else ".show queries | sort by StartedOn" ) return self.management_query_request(database_name, mgmt_query) def running_search_queries_list_request(self, database_name: str, client_activity_id: str) -> dict[str, Any]: """ List currently running search queries on the given database. When client_activity_id argument is set, the request will retrieve information regarding specific running search query. Args: database_name (str): The name of the database to see the running queries. client_activity_id (str): Client-specified identity of the request. Returns: Dict[str, Any]: API response from Azure. """ mgmt_query = ( f".show running queries | where ClientActivityId=='{client_activity_id}'" if client_activity_id else ".show running queries | sort by StartedOn" ) return self.management_query_request(database_name, mgmt_query) def running_search_query_delete_request(self, database_name: str, client_activity_id: str, reason: str) -> dict[str, Any]: """ Starts a best-effort attempt to cancel a specific running search query on the given database. Args: database_name (str): The name of the database to see the completed queries on. client_activity_id (str): Client specified identity of the request. reason (str): The reason for the cancellation. Returns: Dict[str, Any]: API response from Azure. """ cancel_running_query = f".cancel query '{client_activity_id}'" if reason: cancel_running_query += f" with ( reason = '{reason}' )" return self.management_query_request(database_name, cancel_running_query) def management_query_request(self, database_name: str, mgmt_query: str) -> dict[str, Any]: """ API call method for management query endpoint. Each requests that uses management query endpoint uses this method. Args: database_name (str): The name of the database to see the completed queries on. mgmt_query (str): Client specified identity of the request. Returns: Dict[str, Any]: API response from Azure. """ data = retrieve_common_request_body(database_name, mgmt_query) response = self.http_request("POST", url_suffix="/v1/rest/mgmt", data=data) return response def search_query_execute_command(client: DataExplorerClient, args: dict[str, Any]) -> CommandResults: """ Execute search query command. Args: client (DataExplorerClient): Azure Data Explorer API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Command results with raw response, outputs and readable outputs. """ query = str(args["query"]) database_name = str(args["database_name"]) timeout = Decimal(args.get("timeout", "5")) if timeout < 0 or timeout > 60: raise ValueError("Timeout argument should be a float number between 0 to 60.") client_activity_id = f"{client.client_activity_prefix};{uuid.uuid4()}" response = client.search_query_execute_request(database_name, query, timeout, client_activity_id) response_kusto_dataset = KustoResponseDataSetV1(response) primary_results = convert_kusto_response_to_dict(response_kusto_dataset) outputs = { "Database": database_name, "Query": query, "ClientActivityID": client_activity_id, "PrimaryResults": primary_results, } readable_output = tableToMarkdown( f"Results of executing search query with client activity ID: {client_activity_id}", primary_results, headerTransform=pascalToSpace, ) command_results = CommandResults( outputs_prefix="AzureDataExplorer.SearchQueryResults", outputs_key_field="ClientActivityID", outputs=outputs, raw_response=response, readable_output=readable_output, ) return command_results def search_queries_list_command(client: DataExplorerClient, args: dict[str, Any]) -> CommandResults: """ List completed search queries command. Args: client (DataExplorerClient): Azure Data Explorer API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Command results with raw response, outputs and readable outputs. """ database_name = str(args["database_name"]) page = arg_to_number(args.get("page", DEFAULT_PAGE_NUMBER)) page_size = arg_to_number(args.get("page_size")) limit = arg_to_number(args.get("limit", DEFAULT_LIMIT)) client_activity_id = str(args.get("client_activity_id", "")) validate_list_command_arguments(page, page_size, limit) # type: ignore[arg-type] response = client.search_queries_list_request(database_name, client_activity_id) return retrieve_command_results_of_list_commands( response, "List of Completed Search Queries", page, # type: ignore[arg-type] page_size, # type: ignore[arg-type] limit, # type: ignore[arg-type] "AzureDataExplorer.SearchQuery", ) def running_search_queries_list_command(client: DataExplorerClient, args: dict[str, Any]) -> CommandResults: """ List currently running search queries command. Args: client (DataExplorerClient): Azure Data Explorer API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Command results with raw response, outputs and readable outputs. """ database_name = str(args["database_name"]) page = arg_to_number(args.get("page", DEFAULT_PAGE_NUMBER)) page_size = arg_to_number(args.get("page_size")) limit = arg_to_number(args.get("limit", DEFAULT_LIMIT)) client_activity_id = str(args.get("client_activity_id", "")) validate_list_command_arguments(page, page_size, limit) # type: ignore[arg-type] response = client.running_search_queries_list_request(database_name, client_activity_id) return retrieve_command_results_of_list_commands( response, "List of Currently running Search Queries", page, # type: ignore[arg-type] page_size, # type: ignore[arg-type] limit, # type: ignore[arg-type] "AzureDataExplorer.RunningSearchQuery", ) def running_search_query_cancel_command(client: DataExplorerClient, args: dict[str, Any]) -> CommandResults: """ Cancel currently running search query command. Args: client (DataExplorerClient): Azure Data Explorer API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Command results with raw response, outputs and readable outputs. """ client_activity_id = str(args["client_activity_id"]) database_name = str(args["database_name"]) reason = str(args.get("reason")) response = client.running_search_query_delete_request(database_name, client_activity_id, reason) response_kusto_dataset = KustoResponseDataSetV1(response) outputs = convert_kusto_response_to_dict(response_kusto_dataset) readable_output = tableToMarkdown( f"Canceled Search Query {client_activity_id}", outputs, headers=["ClientRequestId", "ReasonPhrase", "RunningQueryCanceled"], headerTransform=pascalToSpace, ) command_results = CommandResults( outputs_prefix="AzureDataExplorer.CanceledSearchQuery", outputs_key_field="ClientRequestId", outputs=outputs, raw_response=response, readable_output=readable_output, ) return command_results def retrieve_command_results_of_list_commands( response: dict[str, Any], base_header: str, page: int, page_size: int, limit: int, outputs_prefix: str ) -> CommandResults: """ Retrieves the command results of list commands. Args: response (Dict[str,Any]): API response from Azure. base_header: (str) Header prefix in the readable output. page (int): Page number. page_size (int): Page size. limit (int): Page size. outputs_prefix (str): Command context outputs prefix. Returns: CommandResults: List Command results. """ response_kusto_dataset = KustoResponseDataSetV1(response) total_rows = response_kusto_dataset.primary_results[0].rows_count outputs = convert_kusto_response_to_dict(response_kusto_dataset, page, page_size, limit) readable_header = format_header_for_list_commands(base_header, total_rows, page, page_size, limit) readable_output = tableToMarkdown( readable_header, outputs, headers=["ClientActivityId", "User", "Text", "Database", "StartedOn", "LastUpdatedOn", "State"], headerTransform=pascalToSpace, ) command_results = CommandResults( outputs_prefix=outputs_prefix, outputs_key_field="ClientActivityId", outputs=outputs, raw_response=response, readable_output=readable_output, ) return command_results """ INTEGRATION HELPER METHODS """ def convert_datetime_fields(raw_data: list[dict]) -> list[dict]: """ Converting datetime fields of the response from the API call to str type (in order to make the response json-serializable). Args: raw_data (List[dict]): Response from API call to azure. Returns: List[dict]: JSON serializable response from API. """ for row in raw_data: for key, value in row.items(): if isinstance(value, datetime): row[key] = value.strftime(DATE_TIME_FORMAT) if isinstance(value, timedelta): row[key] = str(value) return raw_data def convert_kusto_response_to_dict( kusto_response: KustoResponseDataSet, page: int = None, page_size: int = None, limit: int = None ) -> list[dict]: """ Converting KustoResponseDataSet object to dict type. Support two use cases of pagination: 'Manual Pagination' and 'Automatic Pagination'. Args: kusto_response (KustoResponseDataSet): The response from API call. page (int): First index to retrieve from. page_size (int) : Number of records to return per page. limit (int): Limit on the number of the results to return. Returns: Dict[str, Any]: Converted response. """ raw_data = kusto_response.primary_results[0].to_dict().get("data", []) if page and page_size: # Manual Pagination from_index = min((page - 1) * page_size, len(raw_data)) to_index = min(from_index + page_size, len(raw_data)) relevant_raw_data = raw_data[from_index:to_index] elif limit: # Automatic Pagination relevant_raw_data = raw_data[: min(len(raw_data), limit)] else: # used only in search query execution command relevant_raw_data = raw_data serialized_data: list[dict] = convert_datetime_fields(relevant_raw_data) return serialized_data def format_header_for_list_commands(base_header: str, rows_count: int, page: int, page_size: int, limit: int) -> str: """ Retrieve the header of the readable output for list commands. Format the header according to the pagination use case: 'Manual Pagination' or 'Automatic Pagination'. Args: base_header (str): The header prefix. rows_count (int): The number of rows in the output. page (int): Client's page number argument. page_size (int): number of records per page. limit (int): Client's limit argument. Returns: Dict[str, Any]: Header for readable output of the command. """ if page_size: total_pages = rows_count // page_size + (rows_count % page_size != 0) if rows_count > 0: base_header += f" \nShowing page {page} out of {total_pages} total pages. Current page size: {page_size}." else: base_header += f" \nShowing 0 to {limit} records out of {rows_count}." return base_header def retrieve_common_request_body(database_name: str, query: str, properties: dict[str, Any] = None) -> dict[str, Any]: """ Retrieve requests body. For every request, the body contains the database name and the query to the execute. Args: database_name (str): The database name. query (str): The query to execute. properties (Dict[str, Any], optional): Other user's properties to send in the request Defaults to None. Returns: Dict[str, Any]: Body raw data for the request. """ data = {"db": database_name, "csl": query} if properties: data["properties"] = properties # type: ignore[assignment] return data def calculate_total_request_timeout(server_timeout: Decimal) -> int: """ Calculates the total timeout duration of a request. Takes into consideration the timeout duration on server side. Args: server_timeout (int): Quesry execution duration on server side. Returns: int: Total timeout duration of a request. """ server_timeout_in_seconds = int(server_timeout * 60) return server_timeout_in_seconds + REQUEST_BASE_TIMEOUT def validate_list_command_arguments(page: int, page_size: int, limit: int) -> None: """ Validation of page number, page size and limit arguments in list commands. Args: page (int): The page number. page_size(int) : Limit on page size. limit (int): Limit on number of records. Raises: ValueError: Error message. """ if not page >= 1 and limit >= 1 and page_size >= 1: raise ValueError("Page and limit arguments must be integers greater than 0.") """ AUTHORIZATION METHODS """ def start_auth(client: DataExplorerClient) -> CommandResults: """ Start the authorization process. Args: client (DataExplorerClient): Azure Data Explorer API client. Returns: CommandResults: authentication guidelines. """ result = client.ms_client.start_auth("!azure-data-explorer-auth-complete") return CommandResults(readable_output=result) def complete_auth(client: DataExplorerClient) -> str: """ Start the authorization process. Args: client (DataExplorerClient): Azure Data Explorer API client. Returns: str: Message about completing the authorization process successfully. """ client.ms_client.get_access_token() return "✅ Authorization completed successfully." def test_connection(client: DataExplorerClient) -> str: """ Test the connection with Azure Data Explorer service. Args: client (DataExplorerClient): Azure Data Explorer API client. Returns: str: Message about successfully connected to the Azure Data Explorer. """ client.ms_client.get_access_token() return "✅ Success!" def test_module(client: DataExplorerClient) -> str: """Tests API connectivity and authentication for client credentials only. 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: client to use :return: 'ok' if test passed. :rtype: ``str`` """ # This should validate all the inputs given in the integration configuration panel, # either manually or by using an API that uses them. if "Device Code" in client.connection_type: raise DemistoException( "Please enable the integration and run `!azure-data-explorer-auth-start`" "and `!azure-data-explorer-auth-complete` to log in." "You can validate the connection by running `!azure-data-explorer-auth-test`\n" "For more details press the (?) button." ) elif client.connection_type == "Client Credentials": client.ms_client.get_access_token() return "ok" else: raise Exception( "When using user auth flow configuration, " "Please enable the integration and run the !azure-data-explorer-auth-test command in order to test it" ) def main() -> None: """ PARSE AND VALIDATE INTEGRATION PARAMS """ params: dict[str, Any] = demisto.params() args: dict[str, Any] = demisto.args() cluster_url = params["cluster_url"] client_id = params["client_id"] client_activity_prefix = params.get("client_activity_prefix") verify_certificate: bool = not params.get("insecure", False) proxy = params.get("proxy", False) enc_key = (params.get("credentials", {})).get("password") tenant_id = params.get("tenant_id") connection_type = params.get("authentication_type", "Device Code") auth_code = (params.get("auth_code", {})).get("password") redirect_uri = params.get("redirect_uri") command = demisto.command() demisto.debug(f"Command being called is {command}") try: requests.packages.urllib3.disable_warnings() # type: ignore[attr-defined] client: DataExplorerClient = DataExplorerClient( cluster_url, client_id, client_activity_prefix, # type: ignore[arg-type] verify_certificate, proxy, connection_type, tenant_id, enc_key, auth_code, redirect_uri, ) commands = { "azure-data-explorer-search-query-execute": search_query_execute_command, "azure-data-explorer-search-query-list": search_queries_list_command, "azure-data-explorer-running-search-query-list": running_search_queries_list_command, "azure-data-explorer-running-search-query-cancel": running_search_query_cancel_command, } if command == "test-module": return_results(test_module(client)) elif command == "azure-data-explorer-generate-login-url": return_results(generate_login_url(client.ms_client)) elif command == "azure-data-explorer-auth-start": return_results(start_auth(client)) elif command == "azure-data-explorer-auth-complete": return_results(complete_auth(client)) elif command == "azure-data-explorer-auth-reset": return_results(reset_auth()) elif command == "azure-data-explorer-auth-test": return_results(test_connection(client)) elif command in commands: return_results(commands[command](client, args)) else: raise NotImplementedError(f"{command} command is not implemented.") except Exception as e: error_text = str(e) if "OneApiErrors" in error_text: error_text = "The execution of search query failed due a client cancel request." elif "Request execution timeout" in error_text: error_text = "Search query execution took longer than the assigned timeout value and has been aborted." return_error(f"Failed to execute {command} command.\nError:\n{error_text}") if __name__ in ("__main__", "__builtin__", "builtins"): main()