Microsoft Graph
Unified gateway to security insights - all from a unified Microsoft Graph Security API.
Analytics & SIEM · Microsoft Graph Security
Details
| ID | Microsoft Graph |
|---|---|
| Provider | Microsoft |
| Category | Analytics & SIEM |
| From Version | 5.0.0 |
| Docker Image | demisto/crypto:1.0.0.10120494 |
| Supported Modules | Agentix Cloud Runtime Security Cloud Posture Security XSIAM EDR Cortex Cloud |
README
⚠️ Microsoft Graph Security Legacy Alerts Deprecation
Note: Microsoft has deprecated the Legacy Alerts API. As of April 10, 2026, the Legacy Alerts endpoint no longer returns data. This integration now exclusively uses the Alerts v2 API. For more details, see the Microsoft documentation.
Unified gateway to security insights - all from a unified Microsoft Graph Security API.
This integration was integrated and tested with version 1.0 of Microsoft Graph.
Authentication
For more details about the authentication used in this integration, see Microsoft Integrations - Authentication
Note: The eDiscovery and Threat Assessment commands are only supported when using the Authorization Code flow with Delegated (work or school account) permission type.
When using the Authorization Code flow for this integration, you should log in as an administrator or a user with administrative privileges (Security Reader or Security Administrator) after running the msg-generate-login-url command and the login window appears. For more information, see here.
Fetch Incidents and Alerts
Use the Fetch incidents type parameter to control what this integration ingests. You can select Alerts, Incidents, or both:
- Alerts - Each Microsoft Graph Security alert is fetched on its own.
- Incidents - Each Microsoft Graph Security incident is fetched on its own, with all of its associated alerts embedded within it (retrieved via
$expand=alerts) and stored as raw JSON. This gives you one grouped incident that already contains its underlying alerts, instead of many separate alert incidents.
You can narrow what is fetched using the Alerts Filter and Incidents Filter parameters. These accept an OData $filter expression, for example severity eq 'medium' and status eq 'active'. For the supported syntax and operators, see Microsoft query parameters.
Important Notes
- Due to API limitations, the msg-search-alerts command does not filter Office 365 provider alerts.\
For more information, see: https://github.com/microsoftgraph/security-api-solutions/issues/56. - The following properties are supported as filters:
- Alerts Filter parameter (and alert filter arguments): assignedTo, classification, determination, createdDateTime, lastUpdateDateTime, severity, serviceSource and status. See Microsoft optional alert query parameters.
- Incidents Filter parameter: assignedTo, classification, createdDateTime, determination, lastUpdateDateTime, severity, and status. See Microsoft optional incident query parameters.
- The filter should be provided as an OData
$filterexpression, for exampleseverity eq 'medium' and status eq 'active'. For the general syntax and operators, see Microsoft query parameters.
- The header include-unknown-enum-members is used when fetching alerts. It ensures that fields with unknown values are correctly mapped to the appropriate service. Learn More.
- When using Threat Assessment, only the following properties are supported as filters for the filter parameter: expectedAssessment, ContentType, status and requestSource.
- When using Threat Assessment for information protection, the following limits apply to any request:
- For email, the resource is a unique network message ID/recipient pair. For example, submitting an email with the same message ID sent to the same person multiple times in a 15 minutes period will trigger the limit per resource limits listed in the following table. However, you can submit up to 150 unique emails every 15 minutes (tenant limit).
Operation Limit per tenant Limit per resource (email, URL, file) POST 150 requests per 15 minutes and 10000 requests per 24 hours. 1 request per 15 minutes and 3 requests per 24 hours.
Required Permissions
- User.Read.All - Application (Only required if using the deprecated commands:
msg-get-userandmsg-get-users) - SecurityIncident.Read.All - Delegated or Application (required for the command
msg-list-security-incidentand Fetch Incidents) - SecurityIncident.ReadWrite.All - Delegated or Application (required for the command
msg-update-security-incidentand Fetch Incidents) - ThreatHunting.Read.All - Delegated or Application (required for the command
msg-advanced-hunting)
Alerts:
- SecurityAlert.Read.All - Application (required for the commands:
msg-search-alertsandmsg-get-alert-details) - SecurityAlert.ReadWrite.All - Application (required for updating alerts with the commands:
msg-update-alertandmsg-create-alert-comment)
eDiscovery:
- eDiscovery.Read.All - Delegated (Required for the
list-ediscoverycommands) - eDiscovery.ReadWrite.All - Delegated (Required for the
create/update/delete-ediscovery,msg-export-result-ediscovery-datacommands) - eDiscovery.Download.Read - Delegated (Required for the msg-list-case-operation, download_file=True command)
More information about defining this permission can be found here.
Threat Assessment:
- Mail.Read.Shared - Delegated
- ThreatAssessment.ReadWrite.All - Delegated
- User.Read.All - Delegated
Configure Microsoft Graph Security on Cortex
- Navigate to Settings > Integrations > Servers & Services.
- Search for Microsoft Graph Security.
-
Click Add instance to create and configure a new integration instance.
Parameter Description Required Azure Cloud When selecting the Custom option, the Host URL parameter must be filled. More information about National clouds can be found here. False Host URL The host URL. When using this parameter, select the Custom option for the Azure Cloud. More information about National clouds can be found here. False Application ID or Client ID The app registration ID. True Token or Tenant ID The tenant ID. True Key or Client Secret The app registration secret. False Authorization code Get the authorization code from steps 3-5 in the self deployed authorization process. False Application redirect URI (for self-deployed mode) The app registration redirect URI. False Certificate Thumbprint Used for certificate authentication, as it appears in the “Certificates & secrets” page of the app. False Private Key Used for certificate authentication. The private key of the registered certificate. False Use Azure Managed Identities Relevant only if the integration is running on Azure VM. If selected, authenticates based on the value provided for the Azure Managed Identities Client ID field. If no value is provided for the Azure Managed Identities Client ID field, authenticates based on the System Assigned Managed Identity. For additional information, see the Help tab. False Azure Managed Identities Client ID The Managed Identities client ID for authentication - relevant only if the integration is running on Azure VM. False Trust any certificate (not secure) Whether to trust any certificate. If True, not secure. False Use system proxy settings Whether to use system proxy settings. False Use a self-deployed Azure application Whether to use a self-deployed application. False Fetch incidents Whether to fetch incidents. False Incident type The incident type to apply. False First fetch timestamp ( <number> <time unit>, e.g., 12 hours, 7 days)<number> <time unit>, for example 1 hour, 30 minutes.False Fetch limit per type The maximum number of items to fetch per type. Applies to both alerts and incidents. Note that when fetching incidents, no more than 50 can be fetched per cycle, due to a Microsoft limitation. False Fetch incidents type Select which record types to fetch. You can select Alerts, Incidents, or both. False Alerts Service Sources Relevant only when fetching Alerts (incidents have no service source). Multiple serviceSourcevalues can be provided, separated by commas, for example “microsoftDefenderForEndpoint,microsoftCloudAppSecurity”. If empty, alerts of all service sources will be fetched.False Alerts Filter Use this field to filter fetched alerts according to their properties. Applies only when fetching Alerts. Overrides the service sources list, if given. Filter should be in the format “{property} eq ‘{property-value}’”. Multiple filters can be applied separated with “ and “, for example “createdDateTime eq YYYY-MM-DD and severity eq ‘high’”. False Incidents Filter Use this field to filter fetched incidents according to their properties. Applies only when fetching Incidents. Filter should be in the format “{property} eq ‘{property-value}’”. Multiple filters can be applied separated with “ and “, for example “severity eq ‘high’ and status eq ‘active’”. False Microsoft 365 Defender context When selected, the hunting query result is also saved to the Microsoft 365 Defender context path. Relevant only for the msg-advanced-hunting command. False - Click Test to validate the URLs, token, and connection.
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.
msg-auth-test
Tests connectivity to Microsoft Graph Security.
Base Command
msg-auth-test
Input
| Argument Name | Description | Required |
|---|---|---|
| permission_type | Which permissions the integration should have. Possible values are: ediscovery, alerts, alerts, ediscovery. Default is ediscovery. | Optional |
Context Output
There is no context output for this command.
Command example
!msg-auth-test permission_type=ediscovery
Human Readable Output
Authentication was successful.
msg-search-alerts
List alerts (security issues) within a customer’s tenant that Microsoft or partner security solutions have identified.
Base Command
msg-search-alerts
Input
| Argument Name | Description | Required |
|---|---|---|
| last_modified | When the alert was last modified in the following string format - YYYY-MM-DD. Possible values are: Last24Hours, Last48Hours, LastWeek. | Optional |
| severity | Alert severity - set by vendor/provider. Possible values are: unknown, informational, low, medium, high. | Optional |
| category | Category of the alert, e.g., credentialTheft, ransomware (Categories can be added or removed by vendors.). | Optional |
| time_from | The start time (creation time of alert) for the search in the following string format - YYYY-MM-DD. | Optional |
| time_to | The end time (creation time of alert) for the search in the following string format - YYYY-MM-DD. | Optional |
| filter | Use this field to filter on any of the alert properties in the format “{property} eq ‘{property-value}’”, e.g. “category eq ‘ransomware’”. | Optional |
| classification | Use this field to filter by alert’s classification. Possible values are: unknown, truePositive, falsePositive, benignPositive. | Optional |
| service_source | Use this field to filter the alerts by the service or product that created this alert. Possible values are: microsoftDefenderForEndpoint, microsoftDefenderForIdentity, microsoftDefenderForOffice365, microsoft365Defender, microsoftAppGovernance, microsoftDefenderForCloudApps. | Optional |
| status | Use this field to filter by alert’s status. Possible values are: unknown, new, inProgress, resolved. | Optional |
| page | Page number to return, zero indexed. | Optional |
| page_size | Number of results in a page. Default is 50. The limit is 2000. | Optional |
| limit | Number of total results to return. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.Alert.ID | string | Alert ID. |
| MsGraph.Alert.Title | string | Alert title. |
| MsGraph.Alert.Category | string | Alert category. |
| MsGraph.Alert.Severity | string | Alert severity. |
| MsGraph.Alert.CreatedDate | date | Alert created date. |
| MsGraph.Alert.Status | string | Alert status. |
| MsGraph.Alert.ActorDisplayName | Unknown | Alert actor name. |
| MsGraph.Alert.AlertWebUrl | String | Alert web URL. |
| MsGraph.Alert.AssignedTo | Unknown | Alert assignee. |
| MsGraph.Alert.Classification | Unknown | Alert classification. |
| MsGraph.Alert.Description | String | Alert description. |
| MsGraph.Alert.DetectionSource | String | Alert detection source. |
| MsGraph.Alert.DetectorId | String | Alert detector ID. |
| MsGraph.Alert.Determination | Unknown | Alert determination. |
| MsGraph.Alert.Evidence.@odata.Type | String | Alert evidence. |
| MsGraph.Alert.Evidence.AzureAdDeviceId | String | Evidence Azure device ID. |
| MsGraph.Alert.Evidence.CreatedDate | Date | Evidence creation time. |
| MsGraph.Alert.Evidence.DefenderAvStatus | String | Evidence Defender AV status. |
| MsGraph.Alert.Evidence.DeviceDnsName | String | Evidence device DNS name. |
| MsGraph.Alert.Evidence.FirstSeenDateTime | Date | Evidence first seen time. |
| MsGraph.Alert.Evidence.HealthStatus | String | Evidence health status. |
| MsGraph.Alert.Evidence.MdeDeviceId | String | Evidence MDE device ID. |
| MsGraph.Alert.Evidence.OnboardingStatus | String | Evidence onboarding status. |
| MsGraph.Alert.Evidence.OsBuild | Number | Evidence OS build. |
| MsGraph.Alert.Evidence.OsPlatform | String | Evidence OS platform. |
| MsGraph.Alert.Evidence.RbacGroupId | Number | Evidence RBAC group ID. |
| MsGraph.Alert.Evidence.RbacGroupName | String | Evidence RBAC group name. |
| MsGraph.Alert.Evidence.RemediationStatus | String | Evidence remediation status. |
| MsGraph.Alert.Evidence.RemediationStatusDetails | Unknown | Evidence remediation status details. |
| MsGraph.Alert.Evidence.RiskScore | String | Evidence risk score. |
| MsGraph.Alert.Evidence.Tags | String | Evidence tags. |
| MsGraph.Alert.Evidence.Verdict | String | Evidence verdict. |
| MsGraph.Alert.Evidence.Version | String | Evidence version. |
| MsGraph.Alert.Evidence.VmMetadata | Unknown | Evidence VM metadata. |
| MsGraph.Alert.FirstActivityDateTime | Date | Evidence first activity time. |
| MsGraph.Alert.IncidentId | String | Alert incident ID. |
| MsGraph.Alert.IncidentWebUrl | String | Alert incident URL. |
| MsGraph.Alert.LastActivityDateTime | Date | Alert last activity time. |
| MsGraph.Alert.LastUpdateDateTime | Date | Alert last update time. |
| MsGraph.Alert.ProviderAlertId | String | Alert provider ID. |
| MsGraph.Alert.RecommendedActions | String | Alert recommended action. |
| MsGraph.Alert.ResolvedDateTime | Date | Alert closing time. |
| MsGraph.Alert.ServiceSource | String | Alert service source. |
| MsGraph.Alert.TenantId | String | Alert tenant ID. |
| MsGraph.Alert.ThreatDisplayName | Unknown | Alert threat display name. |
| MsGraph.Alert.ThreatFamilyName | Unknown | Alert threat family name. |
Human Readable Output
Microsoft Security Graph Alerts
ID IncidentId Status Severity DetectionSource ServiceSource Title Category CreatedDate LastUpdateDateTime id new medium customTi microsoftDefenderForEndpoint test alert None 2022-10-03T03:39:21.7562976Z 2023-04-17T11:01:31.7566667Z
msg-get-alert-details
Get details for a specific alert.
Base Command
msg-get-alert-details
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The alert ID - Provider-generated GUID/unique identifier. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.Alert.ID | string | Alert ID. |
| MsGraph.Alert.Title | string | Alert title. |
| MsGraph.Alert.Category | string | Alert category. |
| MsGraph.Alert.Severity | string | Alert severity. |
| MsGraph.Alert.CreatedDate | date | Alert created date. |
| MsGraph.Alert.Status | string | Alert status. |
| MsGraph.Alert.@odata.Context | String | Alert odata context. |
| MsGraph.Alert.ActorDisplayName | Unknown | Alert actor name. |
| MsGraph.Alert.AlertWebUrl | String | Alert web URL. |
| MsGraph.Alert.AssignedTo | Unknown | Alert assignee. |
| MsGraph.Alert.Classification | Unknown | Alert classification. |
| MsGraph.Alert.Comments.Comment | String | Alert comment. |
| MsGraph.Alert.Comments.CreatedByDisplayName | String | Alert comment creator name. |
| MsGraph.Alert.Comments.CreatedDate | Date | Alert comment creation time. |
| MsGraph.Alert.Description | String | Alert description. |
| MsGraph.Alert.DetectionSource | String | Alert detection source. |
| MsGraph.Alert.DetectorId | String | Alert detector ID. |
| MsGraph.Alert.Determination | Unknown | Alert determination. |
| MsGraph.Alert.Evidence.@odata.Type | String | Alert evidence. |
| MsGraph.Alert.Evidence.CreatedDate | Date | Evidence creation time. |
| MsGraph.Alert.Evidence.DetectionStatus | Unknown | Evidence detection status. |
| MsGraph.Alert.Evidence.ImageFile.FileName | String | Evidence image file name. |
| MsGraph.Alert.Evidence.ImageFile.FilePath | String | Evidence image file path. |
| MsGraph.Alert.Evidence.ImageFile.FilePublisher | Unknown | Evidence image file publisher. |
| MsGraph.Alert.Evidence.ImageFile.FileSize | Unknown | Evidence image file size. |
| MsGraph.Alert.Evidence.ImageFile.Issuer | Unknown | Evidence image file issuer. |
| MsGraph.Alert.Evidence.ImageFile.Sha1 | String | Evidence image file SHA1 hash. |
| MsGraph.Alert.Evidence.ImageFile.Sha256 | String | Evidence image file SHA256 hash. |
| MsGraph.Alert.Evidence.ImageFile.Signer | Unknown | Evidence image file signer. |
| MsGraph.Alert.Evidence.MdeDeviceId | Unknown | Evidence MDE device ID. |
| MsGraph.Alert.Evidence.ParentProcessCreationDateTime | Date | Evidence parent process creation time. |
| MsGraph.Alert.Evidence.ParentProcessId | Number | Evidence parent process process ID. |
| MsGraph.Alert.Evidence.ParentProcessImageFile | Unknown | Evidence parent process image file. |
| MsGraph.Alert.Evidence.ProcessCommandLine | String | Evidence process command line. |
| MsGraph.Alert.Evidence.ProcessCreationDateTime | Date | Evidence process creation time. |
| MsGraph.Alert.Evidence.ProcessId | Number | Evidence process ID. |
| MsGraph.Alert.Evidence.RemediationStatus | String | Evidence remediation status. |
| MsGraph.Alert.Evidence.RemediationStatusDetails | Unknown | Evidence remediation status details. |
| MsGraph.Alert.Evidence.UserAccount.AccountName | String | Evidence user account name. |
| MsGraph.Alert.Evidence.UserAccount.AzureAdUserId | Unknown | Evidence user account Azure AD user ID. |
| MsGraph.Alert.Evidence.UserAccount.DisplayName | String | Evidence user account display name. |
| MsGraph.Alert.Evidence.UserAccount.DomainName | Unknown | Evidence user account domain name. |
| MsGraph.Alert.Evidence.UserAccount.UserPrincipalName | Unknown | Evidence user account user principal name. |
| MsGraph.Alert.Evidence.UserAccount.UserSid | String | Evidence user account user SID. |
| MsGraph.Alert.Evidence.Verdict | String | Evidence verdict. |
| MsGraph.Alert.Evidence.FileDetails.FileName | String | Evidence file details file name. |
| MsGraph.Alert.Evidence.FileDetails.FilePath | String | Evidence file details file path. |
| MsGraph.Alert.Evidence.FileDetails.FilePublisher | Unknown | Evidence file details file publisher. |
| MsGraph.Alert.Evidence.FileDetails.FileSize | Unknown | Evidence file details file size. |
| MsGraph.Alert.Evidence.FileDetails.Issuer | Unknown | Evidence file details file issuer. |
| MsGraph.Alert.Evidence.FileDetails.Sha1 | String | Evidence file details SHA1 hash. |
| MsGraph.Alert.Evidence.FileDetails.Sha256 | String | Evidence file details SHA256 hash. |
| MsGraph.Alert.Evidence.FileDetails.Signer | Unknown | Evidence file details file signer. |
| MsGraph.Alert.Evidence.CֹountryLetterCode | Unknown | Evidence country letter code. |
| MsGraph.Alert.Evidence.IpAddress | String | Evidence IP address. |
| MsGraph.Alert.Evidence.AzureAdDeviceId | Unknown | Evidence Azure AD device ID. |
| MsGraph.Alert.Evidence.DefenderAvStatus | String | Evidence Defender AV status. |
| MsGraph.Alert.Evidence.DeviceDnsName | String | Evidence device DNS name. |
| MsGraph.Alert.Evidence.FirstSeenDateTime | Date | Evidence first seen time. |
| MsGraph.Alert.Evidence.HealthStatus | String | Evidence health status. |
| MsGraph.Alert.Evidence.OnboardingStatus | String | Evidence onboarding status. |
| MsGraph.Alert.Evidence.OsBuild | Unknown | Evidence OS build. |
| MsGraph.Alert.Evidence.OsPlatform | String | Evidence OS platform. |
| MsGraph.Alert.Evidence.RbacGroupId | Number | Evidence RBAC group ID. |
| MsGraph.Alert.Evidence.RbacGroupName | String | Evidence RBAC group name. |
| MsGraph.Alert.Evidence.RiskScore | String | Evidence risk score. |
| MsGraph.Alert.Evidence.Version | String | Evidence version. |
| MsGraph.Alert.Evidence.VmMetadata | Unknown | Evidence VM metadata. |
| MsGraph.Alert.FirstActivityDateTime | Date | Evidence first activity time. |
| MsGraph.Alert.IncidentId | String | Alert incident ID. |
| MsGraph.Alert.IncidentWebUrl | String | Alert incident URL. |
| MsGraph.Alert.LastActivityDateTime | Date | Alert last activity time. |
| MsGraph.Alert.LastUpdateDateTime | Date | Alert last update time. |
| MsGraph.Alert.ProviderAlertId | String | Alert provider ID. |
| MsGraph.Alert.RecommendedActions | String | Alert recommended action. |
| MsGraph.Alert.ResolvedDateTime | Date | Alert closing time. |
| MsGraph.Alert.ServiceSource | String | Alert service source. |
| MsGraph.Alert.TenantId | String | Alert tenant ID. |
| MsGraph.Alert.ThreatDisplayName | Unknown | Alert threat display name. |
| MsGraph.Alert.ThreatFamilyName | Unknown | Alert threat family name. |
Human Readable Output
Microsoft Security Graph Alert Details -
ID IncidentId Status Severity DetectionSource ServiceSource Title Category CreatedDate LastUpdateDateTime new medium customTi microsoftDefenderForEndpoint test alert None 2022-10-03T03:39:21.7562976Z 2023-04-17T11:01:31.7566667Z
msg-update-alert
Update an editable alert property within any integrated solution to keep alert status and assignments in sync across solutions using its reference ID.
Base Command
msg-update-alert
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The Alert ID. Provider-generated GUID/unique identifier. | Required |
| assigned_to | Name of the analyst the alert is assigned to for triage, investigation, or remediation. | Optional |
| status | Alert lifecycle status (stage). Possible values are: unknown, newAlert, inProgress, resolved, new. | Optional |
| classification | Use this field to update the alert’s classification. Possible values are: unknown, truePositive, falsePositive, informationalExpectedActivity. | Optional |
| determination | Use this field to update the alert’s determination. Possible values are: unknown, malware, phishing, other, securityTesting, multiStagedAttack, maliciousUserActivity, lineOfBusinessApplication, unwantedSoftware. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.Alert.ID | string | Alert ID. |
| MsGraph.Alert.Status | string | Alert status, will appear only if changed. |
Human Readable Output
Alert has been successfully updated.
msg-create-alert-comment
Create a comment for an existing alert.
Base Command
msg-create-alert-comment
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The Alert ID - Provider-generated GUID/unique identifier. | Required |
| comment | The comment to add to each alert. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.AlertComment.ID | String | The alert ID that the comment was added to. |
| MsGraph.AlertComment.Comments.Comment | String | The comment itself |
| MsGraph.AlertComment.Comments.CreatedByDisplayName | String | The comment’s creator display name |
| MsGraph.AlertComment.Comments.CreatedDate | Date | The comment’s creation time |
Human Readable Output
Microsoft Security Graph Create Alert Comment -
comment createdByDisplayName createdDate comment Cortex XSOAR MS Graph Dev 2023-04-17T10:57:18.5231438Z comment Cortex XSOAR MS Graph Dev 2023-04-17T11:01:31.7427859Z comment Cortex XSOAR MS Graph Dev 2023-04-17T13:30:22.3995128Z
ms-graph-security-auth-reset
Run this command if for some reason you need to rerun the authentication process.
Base Command
ms-graph-security-auth-reset
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
eDiscovery Commands
msg-list-ediscovery-cases
Lists edicovery cases.
Base Command
msg-list-ediscovery-cases
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. If provided, only this id will be returned. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| all_results | Show all results if true. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoveryCase.CaseId | String | The ID of the eDiscovery case. |
| MsGraph.eDiscoveryCase.CaseStatus | String | The case status. Possible values are: unknown, active, pendingDelete, closing, closed, and closedWithError. |
| MsGraph.eDiscoveryCase.CreatedDateTime | Date | The date and time when the entity was created. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z |
| MsGraph.eDiscoveryCase.Description | String | The case description. |
| MsGraph.eDiscoveryCase.DisplayName | String | The case name. |
| MsGraph.eDiscoveryCase.ExternalId | String | The external case number for customer reference. |
| MsGraph.eDiscoveryCase.LastModifiedDateTime | Date | The latest date and time when the case was modified. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z |
| MsGraph.eDiscoveryCase.ClosedBy.User.DisplayName | String | The user who closed the case. |
| MsGraph.eDiscoveryCase.LastModifiedBy.User.DisplayName | String | The user who last modified the case. |
| MsGraph.eDiscoveryCase.ClosedDateTime | Date | The date and time when the case was closed. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z |
Command example
!msg-list-ediscovery-cases limit=5
Context Example
{
"MsGraph": {
"eDiscoveryCase": [
{
"CaseId": "06386565-47d4-410b-96f9-007978319c02",
"CaseStatus": "active",
"ClosedBy": {
"User": {
"DisplayName": ""
}
},
"CreatedDateTime": "2023-06-18T10:55:12.63Z",
"Description": "",
"DisplayName": "a",
"ExternalId": "123",
"LastModifiedBy": {
"User": {
"DisplayName": "Content Test"
}
},
"LastModifiedDateTime": "2023-06-20T12:25:05.797Z"
},
{
"CaseId": "44bbe68b-0da1-42b4-9ad0-00e8b52f64e2",
"CaseStatus": "active",
"ClosedBy": {
"User": {
"DisplayName": ""
}
},
"CreatedDateTime": "2023-06-18T11:59:33.44Z",
"Description": "",
"DisplayName": "asassdda",
"ExternalId": "",
"LastModifiedBy": {
"User": {
"DisplayName": "Content Test"
}
},
"LastModifiedDateTime": "2023-06-18T11:59:33.44Z"
},
{
"CaseId": "f108b7fa-d177-438e-9679-01cd79e3df3f",
"CaseStatus": "active",
"ClosedBy": {
"User": {
"DisplayName": ""
}
},
"CreatedDateTime": "2023-06-20T07:08:01.95Z",
"Description": "wrking",
"DisplayName": "justw orkok?",
"ExternalId": "",
"LastModifiedBy": {
"User": {
"DisplayName": "Content Test"
}
},
"LastModifiedDateTime": "2023-06-20T07:08:01.95Z"
},
{
"CaseId": "f346c6f5-1d66-4fab-a46b-0abc99c2cef0",
"CaseStatus": "active",
"ClosedBy": {
"User": {
"DisplayName": ""
}
},
"CreatedDateTime": "2023-06-18T11:54:59.873Z",
"Description": "",
"DisplayName": "asasdda",
"ExternalId": "",
"LastModifiedBy": {
"User": {
"DisplayName": "Content Test"
}
},
"LastModifiedDateTime": "2023-06-18T11:54:59.873Z"
},
{
"CaseId": "1a346a94-5220-46ae-a821-0bbbadf4009d",
"CaseStatus": "active",
"ClosedBy": {
"User": {
"DisplayName": ""
}
},
"CreatedDateTime": "2023-06-12T07:05:27.557Z",
"Description": "Test Case 104 description",
"DisplayName": "Test Case 104",
"ExternalId": "",
"LastModifiedBy": {
"User": {
"DisplayName": "Content Test"
}
},
"LastModifiedDateTime": "2023-06-12T07:05:27.557Z"
}
]
}
}
Human Readable Output
Results
Display Name Description External Id Case Status Case Id Created Date Time Last Modified Date Time Last Modified By Name a 123 active 06386565-47d4-410b-96f9-007978319c02 2023-06-18T10:55:12.63Z 2023-06-20T12:25:05.797Z Content Test asassdda active 44bbe68b-0da1-42b4-9ad0-00e8b52f64e2 2023-06-18T11:59:33.44Z 2023-06-18T11:59:33.44Z Content Test justw orkok? wrking active f108b7fa-d177-438e-9679-01cd79e3df3f 2023-06-20T07:08:01.95Z 2023-06-20T07:08:01.95Z Content Test asasdda active f346c6f5-1d66-4fab-a46b-0abc99c2cef0 2023-06-18T11:54:59.873Z 2023-06-18T11:54:59.873Z Content Test Test Case 104 Test Case 104 description active 1a346a94-5220-46ae-a821-0bbbadf4009d 2023-06-12T07:05:27.557Z 2023-06-12T07:05:27.557Z Content Test
msg-create-ediscovery-case
Create a new eDiscovery case. This command only creates an eDiscovery (Premium) case using the new case format. To learn more about the new case format in eDiscovery, see https://learn.microsoft.com/en-us/microsoft-365/compliance/advanced-ediscovery-new-case-format.
Base Command
msg-create-ediscovery-case
Input
| Argument Name | Description | Required |
|---|---|---|
| display_name | The name of the eDiscovery case. | Required |
| description | The case description. | Optional |
| external_id | The external case number for customer reference. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoveryCase.CaseId | String | The ID of the eDiscovery case. |
| MsGraph.eDiscoveryCase.CaseStatus | String | The case status. Possible values are unknown, active, pendingDelete, closing, closed, and closedWithError. |
| MsGraph.eDiscoveryCase.CreatedDateTime | Date | The date and time when the entity was created. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z |
| MsGraph.eDiscoveryCase.Description | String | The case description. |
| MsGraph.eDiscoveryCase.DisplayName | String | The case name. |
| MsGraph.eDiscoveryCase.ExternalId | String | The external case number for customer reference. |
| MsGraph.eDiscoveryCase.LastModifiedDateTime | Date | The latest date and time when the case was modified. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z |
Command example
!msg-create-ediscovery-case display_name=`my case name11234` external_id=123 description=`description of the case`
Context Example
{
"MsGraph": {
"eDiscoveryCase": {
"CaseId": "6dfd17fe-43c5-411f-a194-abdc9492bfa0",
"CaseStatus": "active",
"CreatedDateTime": "2023-07-06T07:42:34.897Z",
"Description": "description of the case",
"DisplayName": "my case name11234",
"ExternalId": "123",
"LastModifiedDateTime": "2023-07-06T07:42:34.897Z"
}
}
}
Human Readable Output
Results
Display Name Description External Id Case Status Case Id Created Date Time Last Modified Date Time my case name11234 description of the case 123 active 6dfd17fe-43c5-411f-a194-abdc9492bfa0 2023-07-06T07:42:34.897Z 2023-07-06T07:42:34.897Z
msg-update-ediscovery-case
Update an eDiscovery case.
Base Command
msg-update-ediscovery-case
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| display_name | The name of the eDiscovery case. | Required |
| description | The case description. | Optional |
| external_id | The external case number for customer reference. | Optional |
Context Output
There is no context output for this command.
Command example
!msg-update-ediscovery-case case_id=6dfd17fe-43c5-411f-a194-abdc9492bfa0 display_name=`new display name` external_id=123 description=`new description of the case`
Human Readable Output
Case with id 6dfd17fe-43c5-411f-a194-abdc9492bfa0 was updated successfully.
msg-close-ediscovery-case
Close an eDiscovery case.
When the legal case or investigation supported by a eDiscovery (Standard) case is completed, you can close the case. Here’s what happens when you close a case:
If the case contains any eDiscovery holds, they’ll be turned off. After the hold is turned off, a 30-day grace period (called a delay hold) is applied to content locations that were on hold. This helps prevent content from being immediately deleted and provides admins the opportunity to search for and restore content before it may be permanently deleted after the delay hold period expires. For more information, see Removing content locations from an eDiscovery hold.
Closing a case only turns off the holds that are associated with that case. If other holds are placed on a content location (such as a Litigation Hold, a retention policy, or a hold from a different eDiscovery (Standard) case) those holds will still be maintained.
The case is still listed on the eDiscovery (Standard) page in the Microsoft Purview compliance portal. The details, holds, searches, and members of a closed case are retained.
You can edit a case after it’s closed. For example, you can add or remove members, create searches, and export search results. The primary difference between active and closed cases is that eDiscovery holds are turned off when a case is closed.
Base Command
msg-close-ediscovery-case
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
Context Output
There is no context output for this command.
Command example
!msg-close-ediscovery-case case_id=6dfd17fe-43c5-411f-a194-abdc9492bfa0
Human Readable Output
Case with id 6dfd17fe-43c5-411f-a194-abdc9492bfa0 was closed successfully.
msg-reopen-ediscovery-case
Reopen an eDiscovery case. When you reopen an eDiscovery (Premium) case, any holds that were in place when the case was closed won’t be automatically reinstated. After the case is reopened, you’ll have to go to the Holds tab and turn on the previous holds. To turn on a hold, select it to display the flyout page, and then set the Status toggle to On.
Base Command
msg-reopen-ediscovery-case
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
Context Output
There is no context output for this command.
Command example
!msg-reopen-ediscovery-case case_id=6dfd17fe-43c5-411f-a194-abdc9492bfa0
Human Readable Output
Case with id 6dfd17fe-43c5-411f-a194-abdc9492bfa0 was reopened successfully.
msg-delete-ediscovery-case
Delete an eDiscovery case. Before you can delete a case, you must first delete all holds listed on the holds page of the case. That includes deleting holds with a status of Off. Default hold policies can only be deleted when the hold is turned off. You must close an active case to turn off any default hold policies in the case. Once the holds are turned off for default hold policies, they can be deleted.
Base Command
msg-delete-ediscovery-case
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
Context Output
There is no context output for this command.
Command example
!msg-delete-ediscovery-case case_id=6dfd17fe-43c5-411f-a194-abdc9492bfa0
Human Readable Output
Case was deleted successfully.
msg-create-ediscovery-custodian
Create a new ediscoveryCustodian object. After the custodian object is created, you will need to create the custodian’s userSource to reference their mailbox and OneDrive for Business site.
Base Command
msg-create-ediscovery-custodian
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| Custodian’s primary SMTP address. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoveryCustodian.CreatedDateTime | Date | Date and time when the custodian was added to the case. |
| MsGraph.eDiscoveryCustodian.CustodianId | String | The ID for the custodian in the specified case. Read-only. |
| MsGraph.eDiscoveryCustodian.CustodianStatus | String | Status of the custodian. Possible values are: active, released. |
| MsGraph.eDiscoveryCustodian.DisplayName | String | Display name of the custodian. |
| MsGraph.eDiscoveryCustodian.Email | String | Email address of the custodian. |
| MsGraph.eDiscoveryCustodian.HoldStatus | String | The hold status of the custodian.The possible values are: notApplied, applied, applying, removing, partial. |
| MsGraph.eDiscoveryCustodian.LastModifiedDateTime | Date | Date and time the custodian object was last modified. |
Command example
!msg-create-ediscovery-custodian case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5 email=testbox2@yoursite.onmicrosoft.com
Context Example
{
"MsGraph": {
"eDiscoveryCustodian": {
"CreatedDateTime": "2023-07-06T07:53:36.9441479Z",
"CustodianId": "0af7ca2b84bc4cff930d5d301cc4caf3",
"CustodianStatus": "active",
"DisplayName": "testbox2",
"Email": "yourmail@yoursite.onmicrosoft.com",
"HoldStatus": "notApplied",
"LastModifiedDateTime": "2023-07-06T07:53:36.9441479Z"
}
}
}
Human Readable Output
Results
Display Name Custodian Status Custodian Id Created Date Time Last Modified Date Time Hold Status testbox2 yourmail@yoursite.onmicrosoft.com active 0af7ca2b84bc4cff930d5d301cc4caf3 2023-07-06T07:53:36.9441479Z 2023-07-06T07:53:36.9441479Z notApplied
msg-list-ediscovery-custodians
List custodians on a given eDiscovery case.
Base Command
msg-list-ediscovery-custodians
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| custodian_id | The ID of the custodian on the given eDiscovery case. If provided, only this ID will be returned. | Optional |
| limit | Number of total results to return. Default is 50. | Optional |
| all_results | Show all results if true. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoveryCustodian.CreatedDateTime | Date | Date and time when the custodian was added to the case. |
| MsGraph.eDiscoveryCustodian.CustodianId | String | The ID for the custodian in the specified case. Read-only. |
| MsGraph.eDiscoveryCustodian.CustodianStatus | String | Status of the custodian. Possible values are: active, released. |
| MsGraph.eDiscoveryCustodian.DisplayName | String | Display name of the custodian. |
| MsGraph.eDiscoveryCustodian.Email | String | Email address of the custodian. |
| MsGraph.eDiscoveryCustodian.HoldStatus | String | The hold status of the custodian. The possible values are: notApplied, applied, applying, removing, partial. |
| MsGraph.eDiscoveryCustodian.LastModifiedDateTime | Date | Date and time the custodian object was last modified. |
| MsGraph.eDiscoveryCustodian.ReleasedDateTime | Date | Date and time the custodian was released from the case. |
Command example
!msg-list-ediscovery-custodians all_results=true case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5
Context Example
{
"MsGraph": {
"eDiscoveryCustodian": {
"CreatedDateTime": "2023-07-06T07:53:36.9441479Z",
"CustodianId": "0af7ca2b84bc4cff930d5d301cc4caf3",
"CustodianStatus": "active",
"DisplayName": "testbox2",
"Email": "mail@yoursite.onmicrosoft.com",
"HoldStatus": "notApplied",
"LastModifiedDateTime": "2023-07-06T07:53:36.9441479Z"
}
}
}
Human Readable Output
Results
Display Name Custodian Status Custodian Id Created Date Time Last Modified Date Time Hold Status testbox2 mail@yoursite.onmicrosoft.com active 0af7ca2b84bc4cff930d5d301cc4caf3 2023-07-06T07:53:36.9441479Z 2023-07-06T07:53:36.9441479Z notApplied
msg-activate-ediscovery-custodian
Activate a custodian that has been released from a case to make them part of the case again. For details, see https://learn.microsoft.com/en-us/microsoft-365/compliance/ediscovery-manage-new-custodians?view=o365-worldwide#re-activate-custodian.
Base Command
msg-activate-ediscovery-custodian
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| custodian_id | The ID of the eDiscovery case. on the given eDiscovery case. | Required |
Context Output
There is no context output for this command.
Command example
!msg-activate-ediscovery-custodian custodian_id=0af7ca2b84bc4cff930d5d301cc4caf3 case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5
Human Readable Output
Custodian with id 0af7ca2b84bc4cff930d5d301cc4caf3 Case was reactivated on case with id 84abfff1-dd69-4559-8f4e-8225e0d505c5 successfully.
msg-release-ediscovery-custodian
Release a custodian from a case. For details, see https://learn.microsoft.com/en-us/microsoft-365/compliance/manage-new-custodians#release-a-custodian-from-a-case.
Base Command
msg-release-ediscovery-custodian
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| custodian_id | The ID of the eDiscovery case. on the given eDiscovery case. | Required |
Context Output
There is no context output for this command.
Command example
!msg-release-ediscovery-custodian custodian_id=0af7ca2b84bc4cff930d5d301cc4caf3 case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5
Human Readable Output
Custodian with id 0af7ca2b84bc4cff930d5d301cc4caf3 was released from case with id 84abfff1-dd69-4559-8f4e-8225e0d505c5 successfully.
msg-create-ediscovery-custodian-site-source
Create a new siteSource object associated with an eDiscovery custodian. Use the msg-list-ediscovery-custodians command in order to get all available custodians.
Base Command
msg-create-ediscovery-custodian-site-source
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| custodian_id | The ID of the eDiscovery case. on the given eDiscovery case. | Required |
| site | URL of the site; for example, https://contoso.sharepoint.com/sites/HumanResources. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.CustodianSiteSource.CreatedBy.Application.DisplayName | String | The name of the application who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedBy.Application.ID | String | The ID of the application who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedBy.User.DisplayName | String | The name of the user who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedBy.User.ID | String | The ID of the user who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedBy.User.UserPrincipalName | String | Internet-style login name of the user who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedDateTime | Date | The date and time the siteSource was created. |
| MsGraph.CustodianSiteSource.DisplayName | String | The display name of the siteSource. This will be the name of the SharePoint site. |
| MsGraph.CustodianSiteSource.HoldStatus | String | The hold status of the siteSource. The possible values are: notApplied, applied, applying, removing, partial. |
| MsGraph.CustodianSiteSource.SiteSourceId | String | The ID of the siteSource. |
Command example
!msg-create-ediscovery-custodian-site-source custodian_id=0af7ca2b84bc4cff930d5d301cc4caf3 case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5 site=https://yourdev.sharepoint.com/sites/site_test_1
Context Example
{
"MsGraph": {
"CustodianSiteSource": {
"CreatedBy": {
"Application": {
"DisplayName": "Cortex XSOAR - MS Graph Security Dev",
"ID": "734f96d8-b19c-4ab1-9382-e04aa9a5debd"
},
"User": {
"DisplayName": "Content Test",
"ID": "38c41451-94b8-44cc-8c02-649208c43b6b",
"UserPrincipalName": "ContentTest@yoursite.onmicrosoft.com"
}
},
"CreatedDateTime": "0001-01-01T00:00:00Z",
"DisplayName": "site_test_1",
"HoldStatus": "notApplied",
"SiteSourceId": "862f0a64-e7db-46e0-a97f-9156b4f693ee"
}
}
}
Human Readable Output
Results
Display Name Site Source Id Hold Status Created Date Time Created By Name Created By UPN Created By App Name site_test_1 862f0a64-e7db-46e0-a97f-9156b4f693ee notApplied 0001-01-01T00:00:00Z Content Test ContentTest@yoursite.onmicrosoft.com Cortex XSOAR - MS Graph Security Dev
msg-create-ediscovery-custodian-user-source
Create a new userSource object associated with an eDiscovery custodian. Use the msg-list-ediscovery-custodians command in order to get all available custodians.
Base Command
msg-create-ediscovery-custodian-user-source
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| custodian_id | The ID of the eDiscovery case. on the given eDiscovery case. | Required |
| SMTP address of the user. | Required | |
| included_sources | Specifies which sources are included in this group. Possible values are: mailbox, site, mailbox, site. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.CustodianUserSource.CreatedBy.Application.DisplayName | String | The name of the application who created the userSource. |
| MsGraph.CustodianUserSource.CreatedBy.Application.ID | String | The ID of the application who created the userSource. |
| MsGraph.CustodianUserSource.CreatedBy.User.DisplayName | String | The name of the user who created the userSource. |
| MsGraph.CustodianUserSource.CreatedBy.User.ID | String | The ID of the user who created the userSource. |
| MsGraph.CustodianUserSource.CreatedBy.User.UserPrincipalName | String | Internet-style login name of the user who created the userSource. |
| MsGraph.CustodianUserSource.CreatedDateTime | Date | The date and time the userSource was created. |
| MsGraph.CustodianUserSource.DisplayName | String | The display name associated with the mailbox and site. |
| MsGraph.CustodianUserSource.Email | String | Email address of the user’s mailbox. |
| MsGraph.CustodianUserSource.HoldStatus | String | The hold status of the userSource. The possible values are: notApplied, applied, applying, removing, partial. |
| MsGraph.CustodianUserSource.IncludedSources | String | Specifies which sources are included in this group. Possible values are: mailbox, site. |
| MsGraph.CustodianUserSource.UserSourceId | String | The ID of the userSource. This is not The ID of the actual group. |
Command example
!msg-create-ediscovery-custodian-user-source custodian_id=0af7ca2b84bc4cff930d5d301cc4caf3 case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5 email=testbox2@yoursite.onmicrosoft.com included_sources="mailbox, site"
Context Example
{
"MsGraph": {
"CustodianUserSource": {
"CreatedBy": {
"Application": {
"DisplayName": "Cortex XSOAR - MS Graph Security Dev",
"ID": "734f96d8-b19c-4ab1-9382-e04aa9a5debd"
},
"User": {
"DisplayName": "Content Test",
"ID": "38c41451-94b8-44cc-8c02-649208c43b6b",
"UserPrincipalName": "ContentTest@yoursite.onmicrosoft.com"
}
},
"CreatedDateTime": "0001-01-01T00:00:00Z",
"DisplayName": "testbox2",
"Email": "testbox2@yoursite.onmicrosoft.com",
"HoldStatus": "notApplied",
"IncludedSources": "mailbox,site",
"UserSourceId": "0af7ca2b-84bc-4cff-930d-5d301cc4caf3"
}
}
}
Human Readable Output
Results
Display Name User Source Id Hold Status Created Date Time Created By Name Created By UPN Created By App Name Included Sources testbox2 testbox2@yoursite.onmicrosoft.com 0af7ca2b-84bc-4cff-930d-5d301cc4caf3 notApplied 0001-01-01T00:00:00Z Content Test ContentTest@yoursite.onmicrosoft.com Cortex XSOAR - MS Graph Security Dev mailbox,site
msg-list-ediscovery-custodian-user-sources
Get a list of the userSource objects associated with an eDiscoveryCustodian. Use the msg-list-ediscovery-custodians command in order to get all available custodians.
Base Command
msg-list-ediscovery-custodian-user-sources
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| custodian_id | The ID of the eDiscovery case. on the given eDiscovery case. | Required |
| user_source_id | The ID of the userSource. If provided, only this id will be returned. | Optional |
| limit | Number of total results to return. Default is 50. | Optional |
| all_results | Show all results if true. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.CustodianUserSource.CreatedBy.Application.DisplayName | String | The name of the application who created the userSource. |
| MsGraph.CustodianUserSource.CreatedBy.Application.ID | String | The ID of the application who created the userSource. |
| MsGraph.CustodianUserSource.CreatedBy.User.DisplayName | String | The name of the user who created the userSource. |
| MsGraph.CustodianUserSource.CreatedBy.User.ID | String | The ID of the user who created the userSource. |
| MsGraph.CustodianUserSource.CreatedBy.User.UserPrincipalName | String | Internet-style login name of the user who created the userSource. |
| MsGraph.CustodianUserSource.CreatedDateTime | Date | The date and time the userSource was created. |
| MsGraph.CustodianUserSource.DisplayName | String | The display name associated with the mailbox and site. |
| MsGraph.CustodianUserSource.Email | String | Email address of the user’s mailbox. |
| MsGraph.CustodianUserSource.HoldStatus | String | The hold status of the userSource. The possible values are: notApplied, applied, applying, removing, partial. |
| MsGraph.CustodianUserSource.IncludedSources | String | Specifies which sources are included in this group. Possible values are: mailbox, site. |
| MsGraph.CustodianUserSource.SiteWebUrl | String | The URL of the user’s OneDrive for Business site. Read-only. |
| MsGraph.CustodianUserSource.UserSourceId | String | The ID of the userSource. This is not The ID of the actual group. |
Command example
!msg-list-ediscovery-custodian-user-sources custodian_id=0af7ca2b84bc4cff930d5d301cc4caf3 case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5
Context Example
{
"MsGraph": {
"CustodianUserSource": {
"CreatedBy": {
"Application": {
"DisplayName": "Cortex XSOAR - MS Graph Security Dev",
"ID": "734f96d8-b19c-4ab1-9382-e04aa9a5debd"
},
"User": {
"DisplayName": "Content Test",
"ID": "38c41451-94b8-44cc-8c02-649208c43b6b",
"UserPrincipalName": "ContentTest@yoursite.onmicrosoft.com"
}
},
"CreatedDateTime": "2023-07-06T08:04:21.1548801Z",
"DisplayName": "testbox2",
"Email": "testbox2@yoursite.onmicrosoft.com",
"HoldStatus": "notApplied",
"IncludedSources": "mailbox,site",
"SiteWebUrl": "https://yourdev-my.sharepoint.com/personal/testbox2_yourdev_onmicrosoft_com",
"UserSourceId": "0af7ca2b-84bc-4cff-930d-5d301cc4caf3"
}
}
}
Human Readable Output
Results
Display Name User Source Id Hold Status Created Date Time Created By Name Created By UPN Created By App Name Site Web Url Included Sources testbox2 testbox2@yoursite.onmicrosoft.com 0af7ca2b-84bc-4cff-930d-5d301cc4caf3 notApplied 2023-07-06T08:04:21.1548801Z Content Test ContentTest@yoursite.onmicrosoft.com Cortex XSOAR - MS Graph Security Dev https://yourdev-my.sharepoint.com/personal/testbox2_yourdev_onmicrosoft_com mailbox,site
msg-list-ediscovery-custodian-site-sources
Get a list of the siteSource objects associated with an eDiscoveryCustodian. Use the msg-list-ediscovery-custodians command in order to get all available custodians.
Base Command
msg-list-ediscovery-custodian-site-sources
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| custodian_id | The ID of the eDiscovery case. on the given eDiscovery case. | Required |
| site_source_id | The ID of the siteSource. If provided, only this id will be returned. | Optional |
| limit | Number of total results to return. Default is 50. | Optional |
| all_results | Show all results if true. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.CustodianSiteSource.CreatedBy.Application.DisplayName | String | The name of the application who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedBy.Application.ID | String | The ID of the application who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedBy.User.DisplayName | String | The name of the user who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedBy.User.ID | String | The ID of the user who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedBy.User.UserPrincipalName | String | Internet-style login name of the user who created the siteSource. |
| MsGraph.CustodianSiteSource.CreatedDateTime | Date | The date and time the siteSource was created. |
| MsGraph.CustodianSiteSource.DisplayName | String | The display name of the siteSource. This will be the name of the SharePoint site. |
| MsGraph.CustodianSiteSource.HoldStatus | String | The hold status of the siteSource. The possible values are: notApplied, applied, applying, removing, partial. |
| MsGraph.CustodianSiteSource.SiteSourceId | String | The ID of the siteSource. |
| MsGraph.CustodianSiteSource.Site.ID | String | The unique identifier of the item. Read-only. |
| MsGraph.CustodianSiteSource.Site.WebUrl | String | URL that displays the item in the browser. Read-only. |
| MsGraph.CustodianSiteSource.Site.CreatedDate | Date | The date and time the siteSource was created. |
Command example
!msg-list-ediscovery-custodian-site-sources custodian_id=0af7ca2b84bc4cff930d5d301cc4caf3 case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5 site_source_id=862f0a64-e7db-46e0-a97f-9156b4f693ee
Context Example
{
"MsGraph": {
"CustodianSiteSource": {
"CreatedBy": {
"User": {
"ID": "38c41451-94b8-44cc-8c02-649208c43b6b"
}
},
"CreatedDateTime": "2023-07-06T08:02:28.5670187Z",
"DisplayName": "site_test_1",
"HoldStatus": "removing",
"Site": {
"CreatedDate": "2023-07-06T08:02:28.5670187Z",
"ID": "862f0a64-e7db-46e0-a97f-9156b4f693ee",
"WebUrl": "https://yourdev.sharepoint.com/sites/site_test_1"
},
"SiteSourceId": "862f0a64-e7db-46e0-a97f-9156b4f693ee"
}
}
}
Human Readable Output
Results
Display Name Site Source Id Hold Status Created Date Time site_test_1 862f0a64-e7db-46e0-a97f-9156b4f693ee removing 2023-07-06T08:02:28.5670187Z
msg-apply-hold-ediscovery-custodian
Start the process of applying hold on eDiscovery custodians.
Available return statuses:
notApplied - The custodian is not on hold (all sources in it are not on hold).
applied - The custodian is on hold (all sources are on hold).
applying - The custodian is in applying hold state (applyHold operation triggered).
removing - The custodian is in removing the hold state(removeHold operation triggered).
partial - The custodian is in mixed state where some sources are on hold and some not on hold or error state.
Base Command
msg-apply-hold-ediscovery-custodian
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| custodian_id | A comma-seperated list of custodians ids to apply a hold to. | Required |
Context Output
There is no context output for this command.
Command example
!msg-apply-hold-ediscovery-custodian custodian_id=09f05c43ffc54ff88cf5c5e89699375d,0af7ca2b84bc4cff930d5d301cc4caf3 case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5
Human Readable Output
Apply hold status is running.
msg-remove-hold-ediscovery-custodian
Start the process of removing hold from eDiscovery custodians.
Base Command
msg-remove-hold-ediscovery-custodian
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| custodian_id | A comma-seperated list of custodians ids to remove a hold from. | Required |
Context Output
There is no context output for this command.
Command example
!msg-remove-hold-ediscovery-custodian custodian_id=09f05c43ffc54ff88cf5c5e89699375d,0af7ca2b84bc4cff930d5d301cc4caf3 case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5
Human Readable Output
Remove hold status is running.
msg-create-ediscovery-non-custodial-data-source
Create a new eDiscoveryNoncustodialDataSource object.
Base Command
msg-create-ediscovery-non-custodial-data-source
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| site | URL of the site, for example, https://contoso.sharepoint.com/sites/HumanResources. | Optional |
| Email address of the user’s mailbox. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.NoncustodialDataSource.CreatedDateTime | Date | Created date and time of the nonCustodialDataSource. |
| MsGraph.NoncustodialDataSource.DataSourceId | String | Unique identifier of the nonCustodialDataSource. |
| MsGraph.NoncustodialDataSource.DataSourceStatus | String | Latest status of the nonCustodialDataSource. Possible values are: Active, Released. |
| MsGraph.NoncustodialDataSource.DisplayName | String | Display name of the noncustodialDataSource. |
| MsGraph.NoncustodialDataSource.HoldStatus | String | The hold status of the nonCustodialDataSource.The possible values are: notApplied, applied, applying, removing, partial |
| MsGraph.NoncustodialDataSource.LastModifiedDateTime | Date | Last modified date and time of the nonCustodialDataSource. |
| MsGraph.NoncustodialDataSource.ReleasedDateTime | Date | Date and time that the nonCustodialDataSource was released from the case. |
Command example
!msg-create-ediscovery-non-custodial-data-source case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5 site=https://yourdev.sharepoint.com/sites/site_test_1
Context Example
{
"MsGraph": {
"NoncustodialDataSource": {
"CreatedDateTime": "2023-07-06T08:22:32.3121523Z",
"DataSourceId": "38394332433939353236344630434633",
"DataSourceStatus": "active",
"DisplayName": "site_test_1",
"HoldStatus": "notApplied",
"LastModifiedDateTime": "2023-07-06T08:22:32.3121523Z",
"ReleasedDateTime": "0001-01-01T00:00:00Z"
}
}
}
Human Readable Output
Results
Created Date Time Data Source Id Data Source Status Display Name Hold Status Last Modified Date Time Released Date Time 2023-07-06T08:22:32.3121523Z 38394332433939353236344630434633 active site_test_1 notApplied 2023-07-06T08:22:32.3121523Z 0001-01-01T00:00:00Z
msg-list-ediscovery-non-custodial-data-sources
Get a list of the non-custodial data sources and their properties.
Base Command
msg-list-ediscovery-non-custodial-data-sources
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| data_source_id | The ID of the dataSource. If provided, only this id will be returned. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| all_results | Show all results if true. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.NoncustodialDataSource.CreatedDateTime | Date | Created date and time of the nonCustodialDataSource. |
| MsGraph.NoncustodialDataSource.DataSourceId | String | Unique identifier of the nonCustodialDataSource. |
| MsGraph.NoncustodialDataSource.DataSourceStatus | String | Latest status of the nonCustodialDataSource. Possible values are: Active, Released. |
| MsGraph.NoncustodialDataSource.DisplayName | String | Display name of the noncustodialDataSource. |
| MsGraph.NoncustodialDataSource.HoldStatus | String | The hold status of the nonCustodialDataSource.The possible values are: notApplied, applied, applying, removing, partial |
| MsGraph.NoncustodialDataSource.LastModifiedDateTime | Date | Last modified date and time of the nonCustodialDataSource. |
| MsGraph.NoncustodialDataSource.ReleasedDateTime | Date | Date and time that the nonCustodialDataSource was released from the case. |
Command example
!msg-list-ediscovery-non-custodial-data-sources case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5
Context Example
{
"MsGraph": {
"NoncustodialDataSource": {
"CreatedDateTime": "2023-07-06T08:22:32.3121523Z",
"DataSourceId": "38394332433939353236344630434633",
"DisplayName": "site_test_1",
"HoldStatus": "notApplied",
"LastModifiedDateTime": "2023-07-06T08:22:32.3121523Z",
"ReleasedDateTime": "0001-01-01T00:00:00Z",
"Status": "active"
}
}
}
Human Readable Output
Results
Display Name Data Source Id Hold Status Created Date Time Last Modified Date Time Released Date Time Status site_test_1 38394332433939353236344630434633 notApplied 2023-07-06T08:22:32.3121523Z 2023-07-06T08:22:32.3121523Z 0001-01-01T00:00:00Z active
msg-create-ediscovery-search
Create a new eDiscoverySearch object.
Base Command
msg-create-ediscovery-search
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| display_name | The display name of the search. | Required |
| description | The description of the search. | Optional |
| content_query | The query string used for the search. The query string format is KQL (Keyword Query Language). For details, see https://learn.microsoft.com/en-us/microsoft-365/compliance/keyword-queries-and-search-conditions.. You can refine searches by using fields paired with values; for example, subject:”Quarterly Financials” AND Date>=06/01/2016 AND Date<=07/01/2016. | Optional |
| data_source_scopes | When specified, the collection will span across a service for an entire workload. Possible values are: none, allTenantMailboxes, allTenantSites, allCaseCustodians, allCaseNoncustodialDataSources. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoverySearch.ContentQuery | String | The query string in KQL (Keyword Query Language) query. For details, see see https://learn.microsoft.com/en-us/microsoft-365/compliance/keyword-queries-and-search-conditions.. You can refine searches by using fields paired with values; for example, subject:”Quarterly Financials” AND Date>=06/01/2016 AND Date<=07/01/2016. |
| MsGraph.eDiscoverySearch.CreatedBy.Application.DisplayName | String | Name of the application who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedBy.Application.ID | String | ID of the application who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedBy.User.DisplayName | String | Name of the user who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedBy.User.ID | String | ID of the user who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedBy.User.UserPrincipalName | String | Internet-style login name of the user who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedDateTime | Date | The date and time the eDiscovery search was created. |
| MsGraph.eDiscoverySearch.DataSourceScopes | String | When specified, the collection will span across a service for an entire workload. Possible values are: none, allTenantMailboxes, allTenantSites, allCaseCustodians, allCaseNoncustodialDataSources. |
| MsGraph.eDiscoverySearch.Description | String | The description of the eDiscovery search. |
| MsGraph.eDiscoverySearch.DisplayName | String | The display name of the eDiscovery search. |
| MsGraph.eDiscoverySearch.LastModifiedDateTime | Date | The last date and time the eDiscovery search was modified. |
| MsGraph.eDiscoverySearch.SearchId | String | The ID for the eDiscovery search. |
Command example
!msg-create-ediscovery-search case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5 display_name=`my search` data_source_scopes=allCaseNoncustodialDataSources
Context Example
{
"MsGraph": {
"eDiscoverySearch": {
"ContentQuery": "",
"CreatedBy": {
"Application": {
"DisplayName": "Cortex XSOAR - MS Graph Security Dev",
"ID": "734f96d8-b19c-4ab1-9382-e04aa9a5debd"
},
"User": {
"DisplayName": "Content Test",
"ID": "38c41451-94b8-44cc-8c02-649208c43b6b",
"UserPrincipalName": "ContentTest@yoursite.onmicrosoft.com"
}
},
"CreatedDateTime": "2023-07-06T08:25:36.9874937Z",
"DataSourceScopes": "allCaseNoncustodialDataSources",
"Description": "",
"DisplayName": "my search",
"LastModifiedDateTime": "2023-07-06T08:25:36.9874937Z",
"SearchId": "e7282eff-ba81-43cb-9027-522a343f6692"
}
}
}
Human Readable Output
Results
Display Name Data Source Scopes Search Id Created By Name Created By App Name Created By UPN Created Date Time Last Modified Date Time my search allCaseNoncustodialDataSources e7282eff-ba81-43cb-9027-522a343f6692 Content Test Cortex XSOAR - MS Graph Security Dev ContentTest@yoursite.onmicrosoft.com 2023-07-06T08:25:36.9874937Z 2023-07-06T08:25:36.9874937Z
msg-update-ediscovery-search
Update an eDiscoverySearch object.
Base Command
msg-update-ediscovery-search
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| search_id | The ID of the eDiscovery search. | Required |
| display_name | The display name of the search. | Required |
| description | The description of the search. | Optional |
| content_query | The query string used for the search. The query string format is KQL (Keyword Query Language). For details, see Keyword queries and search conditions for Content Search and eDiscovery. You can refine searches by using fields paired with values, for example, subject:”Quarterly Financials” AND Date>=06/01/2016 AND Date<=07/01/2016. | Optional |
| data_source_scopes | When specified, the collection will span across a service for an entire workload. Possible values are: none, allTenantMailboxes, allTenantSites, allCaseCustodians, allCaseNoncustodialDataSources. | Optional |
Context Output
There is no context output for this command.
Command example
!msg-update-ediscovery-search case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5 display_name=newname search_id=e7282eff-ba81-43cb-9027-522a343f6692
Human Readable Output
eDiscovery search e7282eff-ba81-43cb-9027-522a343f6692 was updated successfully.
msg-list-ediscovery-searchs
Get the list of eDiscoverySearch resources from an eDiscovery case.
Base Command
msg-list-ediscovery-searchs
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| search_id | The ID of the eDiscovery search. If provided, only this id will be returned. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| all_results | Show all results if true. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoverySearch.ContentQuery | String | The query string in KQL (Keyword Query Language) query. For details, see Keyword queries and search conditions for Content Search and eDiscovery. You can refine searches by using fields paired with values; for example, subject:”Quarterly Financials” AND Date>=06/01/2016 AND Date<=07/01/2016. |
| MsGraph.eDiscoverySearch.CreatedBy.Application.DisplayName | String | Name of the application who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedBy.Application.ID | String | ID of the application who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedBy.User.DisplayName | String | Name of the user who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedBy.User.ID | String | ID of the user who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedBy.User.UserPrincipalName | String | Internet-style login name of the user who created the eDiscovery search. |
| MsGraph.eDiscoverySearch.CreatedDateTime | Date | The date and time the eDiscovery search was created. |
| MsGraph.eDiscoverySearch.DataSourceScopes | String | When specified, the collection will span across a service for an entire workload. Possible values are: none, allTenantMailboxes, allTenantSites, allCaseCustodians, allCaseNoncustodialDataSources. |
| MsGraph.eDiscoverySearch.Description | String | The description of the eDiscovery search. |
| MsGraph.eDiscoverySearch.DisplayName | String | The display name of the eDiscovery search. |
| MsGraph.eDiscoverySearch.SearchId | String | The ID for the eDiscovery search. |
| MsGraph.eDiscoverySearch.LastModifiedDateTime | String | The last date and time the eDiscovery search was modified. |
| MsGraph.eDiscoverySearch.LastModifiedBy.Application.DisplayName | String | Name of the application who last modified the eDiscovery search. |
| MsGraph.eDiscoverySearch.LastModifiedBy.Application.ID | String | ID of the application who last modified the eDiscovery search. |
| MsGraph.eDiscoverySearch.LastModifiedBy.User.DisplayName | String | Name of the user who last modified the eDiscovery search. |
| MsGraph.eDiscoverySearch.LastModifiedBy.User.ID | String | ID of the user who last modified the eDiscovery search. |
| MsGraph.eDiscoverySearch.LastModifiedBy.User.UserPrincipalName | String | Internet-style login name of the user who last modified the eDiscovery search. |
Command example
!msg-list-ediscovery-searchs case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5
Context Example
{
"MsGraph": {
"eDiscoverySearch": {
"ContentQuery": "",
"CreatedBy": {
"Application": {
"DisplayName": "Cortex XSOAR - MS Graph Security Dev",
"ID": "734f96d8-b19c-4ab1-9382-e04aa9a5debd"
},
"User": {
"DisplayName": "Content Test",
"ID": "38c41451-94b8-44cc-8c02-649208c43b6b",
"UserPrincipalName": "ContentTest@yoursite.onmicrosoft.com"
}
},
"CreatedDateTime": "2023-07-06T08:25:36.9874937Z",
"DataSourceScopes": "allCaseNoncustodialDataSources",
"Description": "",
"DisplayName": "newname",
"LastModifiedBy": {
"Application": {
"DisplayName": "Cortex XSOAR - MS Graph Security Dev",
"ID": "734f96d8-b19c-4ab1-9382-e04aa9a5debd"
},
"User": {
"DisplayName": "Content Test",
"ID": "38c41451-94b8-44cc-8c02-649208c43b6b",
"UserPrincipalName": "ContentTest@yoursite.onmicrosoft.com"
}
},
"LastModifiedDateTime": "2023-07-06T08:27:51.5611704Z",
"SearchId": "e7282eff-ba81-43cb-9027-522a343f6692"
}
}
}
Human Readable Output
Results
Display Name Data Source Scopes Search Id Created By Name Created By App Name Created By UPN Created Date Time Last Modified Date Time newname allCaseNoncustodialDataSources e7282eff-ba81-43cb-9027-522a343f6692 Content Test Cortex XSOAR - MS Graph Security Dev ContentTest@yoursite.onmicrosoft.com 2023-07-06T08:25:36.9874937Z 2023-07-06T08:27:51.5611704Z
msg-purge-ediscovery-data
Deletes mailbox items in Exchange or messages in Microsoft Teams that are included in an eDiscovery search.
You can collect and purge the following categories of Teams content:
Teams 1:1 chats - Chat messages, posts, and attachments shared in a Teams conversation between two people. Teams 1:1 chats are also called conversations.
Teams group chats - Chat messages, posts, and attachments shared in a Teams conversation between three or more people. Also called 1:N chats or group conversations.
Teams channels - Chat messages, posts, replies, and attachments shared in a standard Teams channel.
Private channels - Message posts, replies, and attachments shared in a private Teams channel.
Shared channels - Message posts, replies, and attachments shared in a shared Teams channel.
Base Command
msg-purge-ediscovery-data
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| search_id | The ID of the eDiscovery search. | Required |
| purge_type | Whether the action is soft delete or hard delete. Possible values are: permanentlyDelete, recoverable. | Optional |
| purge_areas | Define the locations to be in scope of the purge action. Possible values are: teamsMessages, mailboxes. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoveryCase.Purge.OperationID | String | The ID of the purge operation. Use this ID to retrieve the operation status via ‘msg-list-case-operation’. |
| MsGraph.eDiscoveryCase.Purge.Status | String | The status of the purge operation. |
Command example
!msg-purge-ediscovery-data case_id=00000000-0000-0000-0000-000000000000 search_id=11111111-1111-1111-1111-111111111111 purge_type=permanentlyDelete purge_areas=mailboxes
Context Example
{
"MsGraph": {
"eDiscoveryCase": {
"Purge": {
"OperationID": "example-operation-id",
"Status": "succeeded"
}
}
}
}
Human Readable Output
eDiscovery purge status is succeeded.
- Operation ID: example-operation-id
msg-delete-ediscovery-search
Delete an eDiscoverySearch object.
Base Command
msg-delete-ediscovery-search
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| search_id | The ID of the eDiscovery search. | Optional |
Context Output
There is no context output for this command.
Command example
!msg-delete-ediscovery-search case_id=84abfff1-dd69-4559-8f4e-8225e0d505c5 search_id=e7282eff-ba81-43cb-9027-522a343f6692
Human Readable Output
eDiscovery search e7282eff-ba81-43cb-9027-522a343f6692 was deleted successfully.
Threat Assessment Commands
msg-create-mail-assessment-request
Create and retrieve a mail threat assessment.
Note:
- The message given in the command’s argument message_id has to contain X-MS-Exchange-Organization-Network-Message-Id header in the message or in the X-MS-Office365-Filtering-Correlation-Id header in quarantined messages.
-
Delegated Mail permissions (Mail.Read or Mail.Read.Shared) are required to access the mail received by the user (recipient email and message user), which means that if the authenticated user is different from the user specified in the recipient_email and message_user, then Read and manage permissions on behalf of the given user need to be added for the authenticated user via Microsoft 365 admin center.
- Go to Microsoft 365 admin center.
- Choose the user email which will be provided in the command’s arguments.
- Click on Manage product licenses.
- Go to Mail.
- Under Mailbox permissions, click on Read and manage permissions.
- click on Add permissions.
- Choose the authenticated user email from the list of given users.
- Click on add.
Base Command
msg-create-mail-assessment-request
Input
| Argument Name | Description | Required |
|---|---|---|
| recipient_email | The email of the user who recieved the mail. | Required |
| expected_assessment | the expected assessment: blocked or unblocked | Required |
| category | The category of the threat: phishing, malware or spam. | Required |
| message_user | Message user, the user’s id or the user’s email. | Required |
| message_id | Message id, Message has to contain ‘X-MS-Exchange-Organization-Network-Message-Id’ header in the message or the ‘X-MS-Office365-Filtering-Correlation-Id’ header in quarantined messages. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| MSGraphMail.MailAssessment.ID | String | Request id. |
| MSGraphMail.MailAssessment.CreatedDateTime | Date | Created data of the threat assessment request. |
| MSGraphMail.MailAssessment.ContentType | String | The content type of threat assessment. |
| MSGraphMail.MailAssessment.ExpectedAssessment | String | The expected assessment from submitter. Possible values are: block, unblock. |
| MSGraphMail.MailAssessment.Category | String | The threat category. Possible values are: spam, phishing, malware. |
| MSGraphMail.MailAssessment.Status | String | The assessment process status. Possible values are: pending, completed. |
| MSGraphMail.MailAssessment.RequestSource | String | The source of threat assessment request. Possible values are: administrator. |
| MSGraphMail.MailAssessment.RecipientEmail | String | The mail recipient whose policies are used to assess the mail. |
| MSGraphMail.MailAssessment.DestinationRoutingReason | String | The reason for mail routed to its destination. Possible values are: none, mailFlowRule, safeSender, blockedSender, advancedSpamFiltering, domainAllowList, domainBlockList, notInAddressBook, firstTimeSender, autoPurgeToInbox, autoPurgeToJunk, autoPurgeToDeleted, outbound, notJunk, junk. |
| MSGraphMail.MailAssessment.MessageID | String | Extracted from the message URI which is The resource URI of the mail message for assessment. |
| MSGraphMail.MailAssessment.CreatedUserID | String | User id. |
| MSGraphMail.MailAssessment.CreatedUsername | String | Username. |
| MSGraphMail.MailAssessment.ResultType | String | Result of the request. |
| MSGraphMail.MailAssessment.ResultMessage | String | Message of the result. |
Command example
!msg-create-mail-assessment-request recipient_email="avishai@demistodev.onmicrosoft.com" expectedAssessment=unblock category=spam user_id=3fa9f28b-eb0e-463a-ba7b-8089fe9991e2 user_message=AAMkAGY3OTQyMzMzLWYxNjktNDE0My05NmZhLWQ5MGY1YjIyNzBkNABGAAAAAACYCKjWAnXBTrnhgWJCcLX7BwDrxRwRjq-zTrN6vWSzK4OWAAAAAAEJAADrxRwRjq-zTrN6vWSzK4OWAAY5aBb-AAA=
Context Example
{
"id": "11922306-b25b-4605-ff0d-08d772fcf996",
"createdDateTime": "2019-11-27T05:45:14.0962061Z",
"contentType": "mail",
"expectedAssessment": "unblock",
"category": "spam",
"status": "completed",
"requestSource": "administrator",
"recipientEmail": "avishai@demistodev.onmicrosoft.com",
"destinationRoutingReason": "notJunk",
"messageUri": "",
"createdBy": {
"user": {
"id": "c52ce8db-3e4b-4181-93c4-7d6b6bffaf60",
"displayName": "Ronald Admin"
}
},
"results": [
{
"id": "63798129-a62c-4f9e-2c6d-08d772fcfb0e",
"createdDateTime": "2019-11-27T05:45:16.55Z",
"resultType": "checkPolicy",
"message": "No policy was hit."
},
{
"id": "d38c2448-79eb-467e-2495-08d772fdb7d1",
"createdDateTime": "2019-11-27T05:50:33.243Z",
"resultType": "rescan",
"message": "Not Spam"
}
]
}
Human Readable Output
Mail assessment request
ID Created DateTime Content Type Expected Assessment Category Status Request Source Recipient Email Destination Routing Reason Created User ID Created Username 11922306-b25b-4605-ff0d-08d772fcf996 “2019-11-27T05:45:14.0962061Z” unblock spam completed administrator avishai@demistodev.onmicrosoft.com notJunk 63798129-a62c-4f9e-2c6d-08d772fcfb0e No policy was hit.
msg-create-email-file-assessment-request
Create and retrieve an email file threat assessment.
Note: File has to contain X-MS-Exchange-Organization-Network-Message-Id header in the message or in the X-MS-Office365-Filtering-Correlation-Id header in quarantined messages.
Base Command
msg-create-email-file-assessment-request
Input
| Argument Name | Description | Required |
|---|---|---|
| recipient_email | The email of the user who recieved the mail. | Required |
| expected_assessment | the expected assessment: blocked or unblocked | Required |
| category | The category of the threat: phishing, malware or spam. | Required |
| content_data | content of an email file. | Optional |
| entry_id | entry id of file uploaded in the war room. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MSGraphMail.EmailAssessment.ID | String | Request id. |
| MSGraphMail.EmailAssessment.CreatedDateTime | Date | Created data of the threat assessment request. |
| MSGraphMail.EmailAssessment.ContentType | String | The content type of threat assessment. |
| MSGraphMail.EmailAssessment.ExpectedAssessment | String | The expected assessment from submitter. Possible values are: block, unblock. |
| MSGraphMail.EmailAssessment.Category | String | The threat category. Possible values are: spam, phishing, malware. |
| MSGraphMail.EmailAssessment.Status | String | The assessment process status. Possible values are: pending, completed. |
| MSGraphMail.EmailAssessment.RequestSource | String | The source of threat assessment request. Possible values are: administrator. |
| MSGraphMail.EmailAssessment.RecipientEmail | String | The mail recipient whose policies are used to assess the mail. |
| MSGraphMail.EmailAssessment.DestinationRoutingReason | String | The reason for mail routed to its destination. Possible values are: none, mailFlowRule, safeSender, blockedSender, advancedSpamFiltering, domainAllowList, domainBlockList, notInAddressBook, firstTimeSender, autoPurgeToInbox, autoPurgeToJunk, autoPurgeToDeleted, outbound, notJunk, junk. |
| MSGraphMail.EmailAssessment.CreatedUserID | String | User id. |
| MSGraphMail.EmailAssessment.CreatedUsername | String | Username. |
| MSGraphMail.EmailAssessment.ResultType | String | Result of the request. |
| MSGraphMail.EmailAssessment.ResultMessage | String | Message of the result. |
Command example
!msg-create-email-file-assessment-request recipient_email="avishai@demistodev.onmicrosoft.com" expectedAssessment=unblock category=phishing entry_id=12359704829584
Context Example
{
"id": "76598306-b25b-4605-ff0d-03kgmtfcf996",
"createdDateTime": "2019-11-27T05:45:14.0962061Z",
"contentType": "mail",
"expectedAssessment": "unblock",
"category": "phishing",
"status": "completed",
"requestSource": "administrator",
"recipientEmail": "avishai@demistodev.onmicrosoft.com",
"destinationRoutingReason": "notJunk",
"createdBy": {
"user": {
"id": "c52ce8db-3e4b-4181-93c4-7d6b6bffaf60",
"displayName": "Ronald Admin"
}
},
"results": [
{
"id": "63798129-a62c-4f9e-2c6d-08d772fcfb0e",
"createdDateTime": "2019-11-27T05:45:16.55Z",
"resultType": "checkPolicy",
"message": "Phishing attempt."
}
]
}
Human Readable Output
Mail assessment request
ID Created DateTime Content Type Expected Assessment Category Status Request Source Recipient Email Destination Routing Reason Created User ID Created Username 76598306-b25b-4605-ff0d-03kgmtfcf996 “2019-11-27T05:45:14.0962061Z” unblock phishing completed administrator avishai@demistodev.onmicrosoft.com notJunk 63798129-a62c-4f9e-2c6d-08d772fcfb0e Phishing attempt.
msg-create-file-assessment-request
Create and retrieve a file threat assessment.
Base Command
msg-create-file-assessment-request
Input
| Argument Name | Description | Required |
|---|---|---|
| file_name | The file name. | Required |
| expected_assessment | the expected assessment: blocked or unblocked | Required |
| category | The category of the threat: phishing, malware or spam. | Required |
| content_data | content of an email file. | Optional |
| entry_id | entry id of file uploaded in the war room. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MSGraphMail.FileAssessment.ID | String | Request id. |
| MSGraphMail.FileAssessment.CreatedDateTime | Date | Created data of the threat assessment request. |
| MSGraphMail.FileAssessment.ContentType | String | The content type of threat assessment. |
| MSGraphMail.FileAssessment.ExpectedAssessment | String | The expected assessment from submitter. Possible values are: block, unblock. |
| MSGraphMail.FileAssessment.Category | String | The threat category. Possible values are: phishing, malware. |
| MSGraphMail.FileAssessment.Status | String | The assessment process status. Possible values are: pending, completed. |
| MSGraphMail.FileAssessment.RequestSource | String | The source of threat assessment request. Possible values are: administrator. |
| MSGraphMail.FileAssessment.FileName | String | The file name. |
| MSGraphMail.FileAssessment.CreatedUserID | String | User id. |
| MSGraphMail.FileAssessment.CreatedUsername | String | Username. |
| MSGraphMail.FileAssessment.ResultType | String | Result of the request. |
| MSGraphMail.FileAssessment.ResultMessage | String | Message of the result. |
Command example
!msg-create-file-assessment-request file_name="test_file.txt" expectedAssessment=block category=phishing entry_id=1235970482958bkf4
Context Example
{
"id": "0796306-b456-4605-ff0d-03kgmtfcf876",
"createdDateTime": "2019-11-27T05:45:14.0962061Z",
"contentType": "file",
"expectedAssessment": "block",
"category": "phishing",
"status": "completed",
"requestSource": "administrator",
"fileName": "test_file.txt",
"createdBy": {
"user": {
"id": "c52ce8db-3e4b-4181-93c4-7d6b6bffaf60",
"displayName": "Ronald Admin"
}
},
"results": [
{
"id": "63798129-a62c-4f9e-2c6d-08d772fcfb0e",
"createdDateTime": "2019-11-27T05:45:16.55Z",
"resultType": "checkPolicy",
"message": "Phishing attempt."
}
]
}
Human Readable Output
Mail assessment request
ID Created DateTime Content Type Expected Assessment Category Status Request Source File Name Created User ID Created Username 0796306-b456-4605-ff0d-03kgmtfcf876 “2019-11-27T05:45:14.0962061Z” file block phishing completed administrator test_file.txt 63798129-a62c-4f9e-2c6d-08d772fcfb0e Phishing attempt.
msg-create-url-assessment-request
Create and retrieve url threat assessment.
Base Command
msg-create-url-assessment-request
Input
| Argument Name | Description | Required |
|---|---|---|
| url | The URL. | Required |
| expected_assessment | the expected assessment: blocked or unblocked | Required |
| category | The category of the threat: phishing, malware or spam. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| MSGraphMail.UrlAssessment.ID | String | Request id. |
| MSGraphMail.UrlAssessment.CreatedDateTime | Date | Created data of the threat assessment request. |
| MSGraphMail.UrlAssessment.ContentType | String | The content type of threat assessment. |
| MSGraphMail.UrlAssessment.ExpectedAssessment | String | The expected assessment from submitter. Possible values are: block, unblock. |
| MSGraphMail.UrlAssessment.Category | String | The threat category. Possible values are: spam, phishing, malware. |
| MSGraphMail.UrlAssessment.Status | String | The assessment process status. Possible values are: pending, completed. |
| MSGraphMail.UrlAssessment.RequestSource | String | The source of threat assessment request. Possible values are: administrator. |
| MSGraphMail.UrlAssessment.Url | String | The url. |
| MSGraphMail.UrlAssessment.CreatedUserID | String | User id. |
| MSGraphMail.UrlAssessment.CreatedUsername | String | Username. |
| MSGraphMail.UrlAssessment.ResultType | String | Result of the request. |
| MSGraphMail.UrlAssessment.ResultMessage | String | Message of the result. |
| MSGraphMail.UrlAssessment.RecipientEmail | String | Recipient Email. |
| MSGraphMail.UrlAssessment.DestinationRoutingReason | String | Destination Routing Reason. |
Command example
!msg-create-url-assessment-request url="httpp://support.clean-mx.de/clean-mx/viruses.php" expectedAssessment=block category=malware
Context Example
{
"id": "0796306-b456-4605-ff0d-03okmtgcf876",
"createdDateTime": "2019-11-27T05:45:14.0962061Z",
"contentType": "url",
"expectedAssessment": "block",
"category": "malware",
"status": "completed",
"requestSource": "administrator",
"url": "httpp://support.clean-mx.de/clean-mx/viruses.php",
"createdBy": {
"user": {
"id": "c52ce8db-3e4b-4181-93c4-7d6b6bffaf60",
"displayName": "Ronald Admin"
}
},
"results": [
{
"id": "63798129-a62c-4f9e-2c6d-08d772fcfb0e",
"createdDateTime": "2019-11-27T05:45:16.55Z",
"resultType": "checkPolicy",
"message": "Malware attempt."
}
]
}
Human Readable Output
Mail assessment request
ID Created DateTime Content Type Expected Assessment Category Status Request Source URL Created User ID Created Username 0796306-b456-4605-ff0d-03okmtgcf876 “2019-11-27T05:45:14.0962061Z” url block malware completed administrator httpp://support.clean-mx.de/clean-mx/viruses.php 63798129-a62c-4f9e-2c6d-08d772fcfb0e Malware attempt.
msg-list-threat-assessment-requests
Retrieve all threat assessment requests.
Base Command
msg-list-threat-assessment-requests
Input
| Argument Name | Description | Required |
|---|---|---|
| request_id | The request id. | Optional |
| filter | Available fields for filter are:expectedAssessment,ContentType,status,requestSource. Example:category eq ‘malware’ | Optional |
| order_by | Drop -down: id, createdDateTime, ContentType, expectedAssessment, category, status, requestSource, category | Optional |
| sort_order | desc or asc. | Optional |
| limit | Default is 50. | Optional |
| next_token | the retrieved token from first run when there’s more data to retrieve. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MSGraphMail.AssessmentRequest.ID | String | Request id. |
| MSGraphMail.AssessmentRequest.CreatedDateTime | Date | Created data of the threat assessment request. |
| MSGraphMail.AssessmentRequest.ContentType | String | The content type of threat assessment. |
| MSGraphMail.AssessmentRequest.ExpectedAssessment | String | The expected assessment from submitter. Possible values are: block, unblock. |
| MSGraphMail.AssessmentRequest.Category | String | The threat category. Possible values are: spam, phishing, malware. |
| MSGraphMail.AssessmentRequest.Status | String | The assessment process status. Possible values are: pending, completed. |
| MSGraphMail.AssessmentRequest.RequestSource | String | The source of threat assessment request. Possible values are: administrator. |
| MSGraphMail.AssessmentRequest.DestinationRoutingReason | String | The destination Routing Reason. |
| MSGraphMail.AssessmentRequest.RecipientEmail | String | The recipient email. |
| MSGraphMail.AssessmentRequest.URL | String | The url. |
| MSGraphMail.AssessmentRequest.FileName | String | The file name. |
| MSGraphMail.AssessmentRequest.CreatedUserID | String | User id. |
| MSGraphMail.AssessmentRequest.CreatedUsername | String | Username. |
| MSGraphMail.AssessmentRequest.ResultType | String | Result of the request. |
| MSGraphMail.AssessmentRequest.ResultMessage | String | Message of the result. |
| MsGraph.AssessmentRequestNextToken.next_token | String | the next token from the previous run. |
Command example
#### Context Example
```json
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#informationProtection/threatAssessmentRequests",
"@odata.nextLink": "https://graph.microsoft.com/v1.0/informationProtection/threatAssessmentRequests?$skiptoken=eyJQYWdlQ29va2llIjoiPHJvdyBpZF9JZGVudGl0",
"value": [
{
"@odata.type": "#microsoft.graph.mailAssessmentRequest",
"id": "49c5ef5b-1f65-444a-e6b9-08d772ea2059",
"createdDateTime": "2019-11-27T03:30:18.6890937Z",
"contentType": "mail",
"expectedAssessment": "block",
"category": "spam",
"status": "pending",
"requestSource": "administrator",
"recipientEmail": "avishaibrandies@microsoft.com",
"destinationRoutingReason": "notJunk",
"messageUri": "https://graph.microsoft.com/v1.0/users/c52ce8db-3e4b-4181-93c4-7d6b6bffaf60/messages/AAMkADU3MWUxOTU0LWNlOTEt=",
"createdBy": {
"user": {
"id": "c52ce8db-3e4b-4181-93c4-7d6b6bffaf60",
"displayName": "Ronald Admin"
}
}
},
{
"@odata.type": "#microsoft.graph.emailFileAssessmentRequest",
"id": "ab2ad9b3-2213-4091-ae0c-08d76ddbcacf",
"createdDateTime": "2019-11-20T17:05:06.4088076Z",
"contentType": "mail",
"expectedAssessment": "block",
"category": "malware",
"status": "completed",
"requestSource": "administrator",
"recipientEmail": "tifc@a830edad9050849EQTPWBJZXODQ.onmicrosoft.com",
"destinationRoutingReason": "notJunk",
"contentData": "",
"createdBy": {
"user": {
"id": "c52ce8db-3e4b-4181-93c4-7d6b6bffaf60",
"displayName": "Ronald Admin"
}
}
}
]
}
Human Readable Output
###Next Token is: eyJQYWdlQ29va2llIjoiPHJvdyBpZF9JZGVudGl
Mail assessment request
ID Created DateTime Content Type Expected Assessment Category Status Request Source Recipient Email Created User ID Created Username destinationRoutingReason 49c5ef5b-1f65-444a-e6b9-08d772ea2059 “2019-11-27T03:30:18.6890937Z” block spam pending administrator avishaibrandies@microsoft.com 63798129-a62c-4f9e-2c6d-08d772fcfb0e spam attempt. notJunk ab2ad9b3-2213-4091-ae0c-08d76ddbcacf 2019-11-20T17:05:06.4088076Z block malware pending administrator avishaibrandies@microsoft.com 63798129-a62c-4f9e-2c6d-08d772fcfb0e Malware attempt. notJunk
msg-generate-login-url
Generate the login URL used for the authorization code flow.
Base Command
msg-generate-login-url
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
There is no context output for this command.
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.
msg-advanced-hunting
Advanced hunting is a threat-hunting tool that uses specially constructed queries to examine the past 30 days of event data in Microsoft Graph Security.
To save result in context to ‘Microsoft365Defender’ as well, you can check the ‘Microsoft 365 Defender context’ checkbox in Instance Setting.
Base Command
msg-advanced-hunting
Input
| Argument Name | Description | Required |
|---|---|---|
| query | Advanced hunting query. | Required |
| limit | Number of entries. Enter -1 for unlimited query, In case a limit also appears in the query, priority will be given to the query. | Optional |
| timeout | The time limit in seconds for the http request to run | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.Hunt.query | String | The query used, also acted as a key. |
| MsGraph.Hunt.results | Unknown | The results of the query. |
| Microsoft365Defender.Hunt.query | String | The query used, also acted as a key. |
| Microsoft365Defender.Hunt.results | Unknown | The results of the query. |
Command example
!msg-advanced-hunting query=AlertInfo limit=1
Context Example
{
"Microsoft365Defender": {
"Hunt": {
"query": "AlertInfo | limit 1 ",
"results": [
{
"AlertId": "abc123",
"AttackTechniques": "",
"Category": "Exfiltration",
"DetectionSource": "Microsoft Data Loss Prevention",
"ServiceSource": "Microsoft Data Loss Prevention",
"Severity": "Medium",
"Timestamp": "2024-03-19T03:00:08Z",
"Title": "DLP policy (Custom policy) matched for email with subject (Splunk Report: High Or Critical Priority Host With Malware - 15 min)"
}
]
}
},
"MsGraph": {
"Hunt": {
"query": "AlertInfo | limit 1 ",
"results": [
{
"AlertId": "abc123",
"AttackTechniques": "",
"Category": "Exfiltration",
"DetectionSource": "Microsoft Data Loss Prevention",
"ServiceSource": "Microsoft Data Loss Prevention",
"Severity": "Medium",
"Timestamp": "2024-03-19T03:00:08Z",
"Title": "DLP policy (Custom policy) matched for email with subject (Splunk Report: High Or Critical Priority Host With Malware - 15 min)"
}
]
}
}
}
Human Readable Output
See Results Above
msg-list-security-incident
Get a list of incident objects that Microsoft 365 Defender created to track attacks in an organization. If you want a specific incident, enter an incident ID.
Base Command
msg-list-security-incident
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident’s ID. | Optional |
| limit | Number of incidents in the list. Maximum is 50. Default is 50. | Optional |
| timeout | The time limit in seconds for the http request to run. Default is 50. | Optional |
| status | The status of the incident. Possible values are: active, redirected, resolved, inProgress, unknownFutureValue, awaitingAction. | Optional |
| assigned_to | Owner of the incident. | Optional |
| severity | Indicates the possible impact on assets. The higher the severity, the greater the impact. Typically higher severity items require the most immediate attention. Possible values are: unknown, informational, low, medium, high, unknownFutureValue. | Optional |
| classification | The specification for the incident. | Optional |
| odata | Filter incidents using ‘odata’ query. | Optional |
| extra_data | Whether to include each incident’s related alerts in the response. Possible values are: true, false. Default is false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.Incident.assignedTo | string | Owner of the incident, or null if no owner is assigned. Free editable text. |
| MsGraph.Incident.classification | string | The specification for the incident. Possible values are unknown, falsePositive, truePositive, informationalExpectedActivity, unknownFutureValue. |
| MsGraph.Incident.comments | string | Array of comments created by the Security Operations (SecOps) team when the incident is managed. |
| MsGraph.Incident.createdDateTime | date | Time when the incident was first created. |
| MsGraph.Incident.customTags | string | Array of custom tags associated with an incident. |
| MsGraph.Incident.description | string | Description of the incident. |
| MsGraph.Incident.determination | string | Specifies the determination of the incident. Possible values are unknown, apt, malware, securityPersonnel, securityTesting, unwantedSoftware, other, multiStagedAttack, compromisedUser, phishing, maliciousUserActivity, clean, insufficientData, confirmedUserActivity, lineOfBusinessApplication, unknownFutureValue. |
| MsGraph.Incident.displayName | string | The incident name. |
| MsGraph.Incident.id | number | Unique identifier to represent the incident. |
| MsGraph.Incident.incidentWebUrl | string | The URL for the incident page in the Microsoft 365 Defender portal. |
| MsGraph.Incident.lastModifiedBy | string | The identity that last modified the incident. |
| MsGraph.Incident.lastUpdateDateTime | string | Time when the incident was last updated. |
| MsGraph.Incident.redirectIncidentId | string | Only populated in case an incident is grouped with another incident, as part of the logic that processes incidents. In such a case, the status property is redirected. |
| MsGraph.Incident.severity | string | Indicates the possible impact on assets. The higher the severity, the greater the impact. Typically higher severity items require the most immediate attention. Possible values are unknown, informational, low, medium, high, unknownFutureValue. |
| MsGraph.Incident.status | string | The status of the incident. Possible values are active, resolved, inProgress, redirected, unknownFutureValue, and awaitingAction. |
| MsGraph.Incident.tenantId | string | The Microsoft Entra tenant in which the alert was created. |
| MsGraph.Incident.systemTags | string | The system tags associated with the incident. |
Command example
!msg-list-security-incident limit=1
Context Example
{
"MsGraph": {
"Incident": {
"@odata.count": 26176,
"value": [
{
"Assigned to": null,
"Classification": "unknown",
"Created date time": "2024-03-19T08:08:33.2533333Z",
"Custom tags": "",
"Determination": "unknown",
"Display name": "DLP policy (Custom policy) matched for email with subject (Splunk Report: High Or Critical Priority Host With Malware - 15 min) involving one user",
"Severity": "medium",
"Status": "active",
"System tags": "",
"Updated date time": "2024-03-19T08:08:33.36Z",
"id": "12345"
}
]
}
}
}
Human Readable Output
Incidents
Display name id Severity Status Assigned to Custom tags System tags Classification Determination Created date time Updated date time DLP policy (Custom policy) matched for email with subject (Splunk Report: High Or Critical Priority Host With Malware - 15 min) involving one user 12345 medium active unknown unknown 2024-03-19T08:08:33.2533333Z 2024-03-19T08:08:33.36Z
msg-update-security-incident
Update the incident with the given ID.
Base Command
msg-update-security-incident
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident’s ID. | Required |
| status | Categorize incidents (as Active, Resolved, or Redirected). Possible values are: active, resolved, redirected, unknownFutureValue. | Optional |
| assigned_to | Owner of the incident. | Optional |
| determination | Determination of the incident. Possible values are: unknown, apt, malware, securityPersonnel, unwantedSoftware, other, multiStagedAttack, compromisedUser, phishing, maliciousUserActivity, notMalicious. | Optional |
| classification | The specification for the incident. Possible values are: unknown, falsePositive, truePositive, informationalExpectedActivity, unknownFutureValue. | Optional |
| severity | Indicates the possible impact on assets. The higher the severity, the bigger the impact. Typically, higher severity items require the most immediate attention. The possible values are: unknown, informational, low, medium, high, unknownFutureValue. | Optional |
| resolving_comment | User input that explains the resolution of the incident and the classification choice. It contains free editable text. | Optional |
| custom_tags | Array of custom tags associated with an incident. | Optional |
| timeout | The time limit in seconds for the http request to run. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.Incident.assignedTo | String | Owner of the incident, or null if no owner is assigned. Free editable text. |
| MsGraph.Incident.classification | String | The specification for the incident. Possible values are unknown, falsePositive, truePositive, informationalExpectedActivity, unknownFutureValue. |
| MsGraph.Incident.comments | String | Array of comments created by the Security Operations (SecOps) team when the incident is managed. |
| MsGraph.Incident.createdDateTime | Date | Time when the incident was first created. |
| MsGraph.Incident.customTags | String | Array of custom tags associated with an incident. |
| MsGraph.Incident.description | String | Description of the incident. |
| MsGraph.Incident.determination | String | Specifies the determination of the incident. Possible values are unknown, apt, malware, securityPersonnel, securityTesting, unwantedSoftware, other, multiStagedAttack, compromisedUser, phishing, maliciousUserActivity, clean, insufficientData, confirmedUserActivity, lineOfBusinessApplication, unknownFutureValue. |
| MsGraph.Incident.displayName | String | The incident name. |
| MsGraph.Incident.id | String | Unique identifier to represent the incident. |
| MsGraph.Incident.incidentWebUrl | String | The URL for the incident page in the Microsoft 365 Defender portal. |
| MsGraph.Incident.lastModifiedBy | String | The identity that last modified the incident. |
| MsGraph.Incident.lastUpdateDateTime | Date | Time when the incident was last updated. |
| MsGraph.Incident.redirectIncidentId | String | Only populated in case an incident is grouped with another incident, as part of the logic that processes incidents. In such a case, the status property is redirected. |
| MsGraph.Incident.severity | String | Indicates the possible impact on assets. The higher the severity, the greater the impact. Typically higher severity items require the most immediate attention. Possible values are unknown, informational, low, medium, high, unknownFutureValue. |
| MsGraph.Incident.status | String | The status of the incident. Possible values are active, resolved, inProgress, redirected, unknownFutureValue, and awaitingAction. |
| MsGraph.Incident.tenantId | String | The Microsoft Entra tenant in which the alert was created. |
| MsGraph.Incident.systemTags | String collection | The system tags associated with the incident. |
Command example
!msg-update-security-incident incident_id=12345
Context Example
{
"MsGraph": {
"Incidents": {
"assignedTo": "test5",
"classification": "unknown",
"comments": [],
"createdDateTime": "2024-03-17T15:50:31.9033333Z",
"customTags": [],
"description": null,
"determination": "unknown",
"displayName": "Exfiltration incident involving one user",
"id": "12345",
"incidentWebUrl": "https://security.microsoft.com/incidents/12345?tid=abc123",
"lastModifiedBy": "Microsoft 365 Defender-AlertCorrelation",
"lastUpdateDateTime": "2024-03-19T07:24:34.7066667Z",
"redirectIncidentId": null,
"severity": "medium",
"status": "active",
"systemTags": [],
"tenantId": "abc123"
}
}
}
Human Readable Output
Updated incident No. 12345
Display name id Severity Status Assigned to Custom tags System tags Classification Determination Created date time Updated date time Exfiltration incident involving one user 12345 medium active test5 unknown unknown 2024-03-17T15:50:31.9033333Z 2024-03-19T07:24:34.7066667Z msg-run-estimate-statistics
Starts an eDiscovery estimate statistics operation in Microsoft Purview. The operation calculates the size and number of items matching the search query.
Base Command
msg-run-estimate-statistics
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| search_id | The ID of the eDiscovery search. | Required |
| statistics_options | Bitwise options that specify the statistics to generate. The possible values are: includeRefiners, includeQueryStats, includeUnindexedStats, advancedIndexing, locationsWithoutHits. The advancedIndexing and locationsWithoutHits values are only considered if includeUnindexedStats is set. Possible values are: includeRefiners, includeQueryStats, includeUnindexedStats, advancedIndexing, locationsWithoutHits. | Optional |
Context Output
There is no context output for this command.
msg-get-last-estimate-statistics-operation
Retrieves the most recent eDiscovery estimate statistics operation for a given search in Microsoft Purview. Use this command after running ‘msg-run-estimate-statistics’ to check the results.
Base Command
msg-get-last-estimate-statistics-operation
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| search_id | The ID of the eDiscovery search. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscovery.EstimateStatistics.CaseID | String | The ID of the eDiscovery case. |
| MsGraph.eDiscovery.EstimateStatistics.SearchID | String | The ID of the eDiscovery search. |
| MsGraph.eDiscovery.EstimateStatistics.OperationID | String | The ID of the last estimate statistics operation. |
| MsGraph.eDiscovery.EstimateStatistics.Status | String | The current status of the last estimate statistics operation (e.g., running, succeeded, failed). |
| MsGraph.eDiscovery.EstimateStatistics.PercentProgress | Number | The percent progress of the last estimate statistics operation. |
| MsGraph.eDiscovery.EstimateStatistics.CreatedDateTime | Date | The date and time when the estimate operation was created. |
| MsGraph.eDiscovery.EstimateStatistics.CompletedDateTime | Date | The date and time when the estimate operation completed. |
| MsGraph.eDiscovery.EstimateStatistics.IndexedItemsCount | Number | The number of indexed items found in the search. |
| MsGraph.eDiscovery.EstimateStatistics.IndexedItemsSize | Number | The total size (in bytes) of indexed items. |
| MsGraph.eDiscovery.EstimateStatistics.UnindexedItemsCount | Number | The number of unindexed items found in the search. |
| MsGraph.eDiscovery.EstimateStatistics.UnindexedItemsSize | Number | The total size (in bytes) of unindexed items. |
| MsGraph.eDiscovery.EstimateStatistics.TotalItemsCount | Number | The total number of items (indexed + unindexed). |
| MsGraph.eDiscovery.EstimateStatistics.TotalItemsSize | Number | The total size (in bytes) of all items (indexed + unindexed). |
msg-list-ediscovery-case-hold-policy
Get a list of the ediscoveryHoldPolicy objects and their properties.
Base Command
msg-list-ediscovery-case-hold-policy
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| hold_policy_id | The ID of the legal hold policy. | Optional |
| limit | Number of total results to return. Default is 50. | Optional |
| all_results | Show all results if true. Possible values are: true, false. Default is false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoveryCase.HoldPolicy.ID | String | The ID of the eDiscovery hold policy. |
| MsGraph.eDiscoveryCase.HoldPolicy.DisplayName | String | The display name of the legal hold. |
| MsGraph.eDiscoveryCase.HoldPolicy.Description | String | The legal hold description. |
| MsGraph.eDiscoveryCase.HoldPolicy.ContentQuery | String | KQL query that specifies content to be held. If empty, all content in the specified locations is held. |
| MsGraph.eDiscoveryCase.HoldPolicy.IsEnabled | Boolean | Indicates whether the hold is enabled and actively holding content. |
| MsGraph.eDiscoveryCase.HoldPolicy.Status | String | The status of the legal hold. Possible values are Pending, Error, Success. |
| MsGraph.eDiscoveryCase.HoldPolicy.Errors | String | Lists any errors that happened while placing the hold. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedDateTime | Date | The date and time the legal hold was created. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedDateTime | Date | The date and time the legal hold was last modified. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Application.ID | String | The unique identifier of the application associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Application.DisplayName | String | The display name of the application associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Application.TenantId | String | The tenant ID of the application identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ApplicationInstance.ID | String | The unique identifier of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ApplicationInstance.DisplayName | String | The display name of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ApplicationInstance.TenantId | String | The tenant ID of the application instance identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Conversation.ID | String | The unique identifier of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Conversation.DisplayName | String | The display name of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Conversation.TenantId | String | The tenant ID of the conversation identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ConversationIdentityType.ID | String | The identifier describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ConversationIdentityType.DisplayName | String | The display name describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ConversationIdentityType.TenantId | String | The tenant ID of the conversation identity type. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Device.ID | String | The unique identifier of the device associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Device.DisplayName | String | The display name of the device associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Device.TenantId | String | The tenant ID of the device identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Encrypted.ID | String | The unique identifier of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Encrypted.DisplayName | String | The display name of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Encrypted.TenantId | String | The tenant ID of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.OnPremises.ID | String | The unique identifier of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.OnPremises.DisplayName | String | The display name of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.OnPremises.TenantId | String | The tenant ID of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Guest.ID | String | The unique identifier of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Guest.DisplayName | String | The display name of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Guest.TenantId | String | The tenant ID of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Phone.ID | String | The unique identifier of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Phone.DisplayName | String | The display name of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Phone.TenantId | String | The tenant ID of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.User.ID | String | The unique identifier of the user associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.User.DisplayName | String | The display name of the user associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.User.TenantId | String | The tenant ID of the user identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Application.ID | String | The unique identifier of the application associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Application.DisplayName | String | The display name of the application associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Application.TenantId | String | The tenant ID of the application identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ApplicationInstance.ID | String | The unique identifier of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ApplicationInstance.DisplayName | String | The display name of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ApplicationInstance.TenantId | String | The tenant ID of the application instance identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Conversation.ID | String | The unique identifier of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Conversation.DisplayName | String | The display name of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Conversation.TenantId | String | The tenant ID of the conversation identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ConversationIdentityType.ID | String | The identifier describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ConversationIdentityType.DisplayName | String | The display name describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ConversationIdentityType.TenantId | String | The tenant ID of the conversation identity type. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Device.ID | String | The unique identifier of the device associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Device.DisplayName | String | The display name of the device associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Device.TenantId | String | The tenant ID of the device identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Encrypted.ID | String | The unique identifier of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Encrypted.DisplayName | String | The display name of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Encrypted.TenantId | String | The tenant ID of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.OnPremises.ID | String | The unique identifier of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.OnPremises.DisplayName | String | The display name of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.OnPremises.TenantId | String | The tenant ID of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Guest.ID | String | The unique identifier of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Guest.DisplayName | String | The display name of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Guest.TenantId | String | The tenant ID of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Phone.ID | String | The unique identifier of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Phone.DisplayName | String | The display name of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Phone.TenantId | String | The tenant ID of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.User.ID | String | The unique identifier of the user associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.User.DisplayName | String | The display name of the user associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.User.TenantId | String | The tenant ID of the user identity associated with this action. |
msg-export-result-ediscovery-data
Export results from an estimated ediscoverySearch. For details, see Manage a collection estimate (https://learn.microsoft.com/en-us/purview/ediscovery-create-draft-collection#manage-a-collection-estimate).
Base Command
msg-export-result-ediscovery-data
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| search_id | The ID of the search. | Required |
| additional_options | The additional options for the export. Possible values are: none, teamsAndYammerConversations, cloudAttachments, allDocumentVersions, subfolderContents, listAttachments, htmlTranscripts, advancedIndexing, allItemsInFolder, includeFolderAndPath, condensePaths, friendlyName, splitSource, includeReport. | Required |
| export_criteria | The portion of the estimate results to be exported. Possible values are: searchHits, partiallyIndexed. | Required |
| export_format | The desired format of the exported emails. Possible values are: pst, msg. | Required |
| cloud_attachment_version | The versions of cloud attachments to include in messages. Possible values are: latest, recent10, recent100, all. Default is latest. | Optional |
| description | The description of the export result. | Optional |
| display_name | The display name of the export result. | Required |
| document_version | The versions of files in SharePoint to include. Possible values are: latest, recent10, recent100, all. Default is latest. | Optional |
| export_location | Location scope for partially indexed items. You can choose to include partially indexed items only in responsive locations with search hits or in all targeted locations. Possible values are: responsiveLocations, nonresponsiveLocations. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoveryCase.Export.OperationID | String | The ID of the export operation. Use this ID to retrieve the operation status via ‘msg-list-case-operation’. |
| MsGraph.eDiscoveryCase.Export.CaseID | String | The ID of the eDiscovery case the export belongs to. |
| MsGraph.eDiscoveryCase.Export.Location | String | The Location header URL returned for the export operation. |
Command example
!msg-export-result-ediscovery-data case_id=00000000-0000-0000-0000-000000000000 search_id=11111111-1111-1111-1111-111111111111 additional_options=none export_criteria=searchHits export_format=pst display_name="Example Export"
Context Example
{
"MsGraph": {
"eDiscoveryCase": {
"Export": {
"CaseID": "00000000-0000-0000-0000-000000000000",
"Location": "https://graph.microsoft.com/v1.0/security/cases/ediscoveryCases/00000000-0000-0000-0000-000000000000/operations/example-operation-id",
"OperationID": "example-operation-id"
}
}
}
}
Human Readable Output
eDiscovery export request was submitted successfully.
- Case ID: 00000000-0000-0000-0000-000000000000
- Operation ID: example-operation-id
msg-delete-ediscovery-case-hold-policy
Delete an eDiscovery hold policy. This command submits a deletion request to Microsoft Graph; the policy may not be removed immediately and can take some time to complete.
Base Command
msg-delete-ediscovery-case-hold-policy
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| hold_policy_id | The policy ID that is intended to be deleted. | Required |
Context Output
There is no context output for this command.
msg-list-case-operation
Get a list of the caseOperation objects and their properties, or retrieve a specific operation by ID. When operation_id is provided, you can set download_file=true to download the export report (if available).
Base Command
msg-list-case-operation
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| operation_id | The ID of the operation. | Optional |
| limit | Number of total results to return. Default is 50. | Optional |
| all_results | Show all results if true. Possible values are: true, false. Default is false. | Optional |
| download_file | Download the export report when an export file link is available. This only applies if operation_id is provided. For authorization or permission errors while downloading, see: https://learn.microsoft.com/en-us/graph/api/security-caseoperation-get?view=graph-rest-1.0&tabs=http#response-1. Possible values are: true, false. Default is false. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoveryCase.Operation.ID | String | The ID of the case operation. Read-only. |
| MsGraph.eDiscoveryCase.Operation.Action | String | The type of action the operation represents (caseAction). Example values: contentExport, applyTags, convertToPdf, index, estimateStatistics, addToReviewSet, holdUpdate, purgeData, exportReport, exportResult, holdPolicySync. |
| MsGraph.eDiscoveryCase.Operation.Status | String | The status of the case operation. Possible values: notStarted, submissionFailed, running, succeeded, partiallySucceeded, failed. |
| MsGraph.eDiscoveryCase.Operation.PercentProgress | Number | The progress of the operation. |
| MsGraph.eDiscoveryCase.Operation.CreatedDateTime | Date | The date and time the operation was created. |
| MsGraph.eDiscoveryCase.Operation.CompletedDateTime | Date | The date and time the operation was completed. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Application.ID | String | The unique identifier of the application associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Application.DisplayName | String | The display name of the application associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Application.TenantId | String | The tenant ID of the application identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.ApplicationInstance.ID | String | The unique identifier of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.ApplicationInstance.DisplayName | String | The display name of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.ApplicationInstance.TenantId | String | The tenant ID of the application instance identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Conversation.ID | String | The unique identifier of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Conversation.DisplayName | String | The display name of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Conversation.TenantId | String | The tenant ID of the conversation identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.ConversationIdentityType.ID | String | The identifier describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.ConversationIdentityType.DisplayName | String | The display name describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.ConversationIdentityType.TenantId | String | The tenant ID of the conversation identity type. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Device.ID | String | The unique identifier of the device associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Device.DisplayName | String | The display name of the device associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Device.TenantId | String | The tenant ID of the device identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Encrypted.ID | String | The unique identifier of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Encrypted.DisplayName | String | The display name of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Encrypted.TenantId | String | The tenant ID of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.OnPremises.ID | String | The unique identifier of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.OnPremises.DisplayName | String | The display name of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.OnPremises.TenantId | String | The tenant ID of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Guest.ID | String | The unique identifier of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Guest.DisplayName | String | The display name of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Guest.TenantId | String | The tenant ID of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Phone.ID | String | The unique identifier of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Phone.DisplayName | String | The display name of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.Phone.TenantId | String | The tenant ID of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.User.ID | String | The unique identifier of the user associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.User.DisplayName | String | The display name of the user associated with this action. |
| MsGraph.eDiscoveryCase.Operation.CreatedBy.User.TenantId | String | The tenant ID of the user identity associated with this action. |
| MsGraph.eDiscoveryCase.Operation.ResultInfo.Code | Number | The result code (2xx success, 4xx client error, 5xx server error). |
| MsGraph.eDiscoveryCase.Operation.ResultInfo.Message | String | The result message. |
| MsGraph.eDiscoveryCase.Operation.ResultInfo.Subcode | Number | The result subcode. |
| File.Size | Number | The size of the file. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA256 hash of the file. |
| File.Name | String | The name of the file. |
| File.SSDeep | String | The SSDeep hash of the file. |
| File.EntryID | String | The entry ID of the file. |
| File.Info | String | File information. |
| File.Type | String | The file type. |
| File.MD5 | String | The MD5 hash of the file. |
| File.Extension | String | The file extension. |
msg-create-ediscovery-case-hold-policy
Create a new ediscoveryHoldPolicy object.
Base Command
msg-create-ediscovery-case-hold-policy
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| display_name | The display name of the legal hold policy. | Required |
| description | The description of the legal hold policy. | Optional |
| content_query | KQL query specifying content to be held in the specified locations. Learn about KQL (https://learn.microsoft.com/en-us/kusto/query/?view=azure-data-explorer&preserve-view=true). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| MsGraph.eDiscoveryCase.HoldPolicy.ID | String | The ID of the eDiscovery hold policy. |
| MsGraph.eDiscoveryCase.HoldPolicy.DisplayName | String | The display name of the legal hold. |
| MsGraph.eDiscoveryCase.HoldPolicy.Description | String | The legal hold description. |
| MsGraph.eDiscoveryCase.HoldPolicy.ContentQuery | String | KQL query that specifies content to be held. If empty, all content in the specified locations is held. |
| MsGraph.eDiscoveryCase.HoldPolicy.IsEnabled | Boolean | Indicates whether the hold is enabled and actively holding content. |
| MsGraph.eDiscoveryCase.HoldPolicy.Status | String | The status of the legal hold. Possible values are Pending, Error, Success. |
| MsGraph.eDiscoveryCase.HoldPolicy.Errors | String | Lists any errors that happened while placing the hold. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedDateTime | Date | The date and time the legal hold was created. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedDateTime | Date | The date and time the legal hold was last modified. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Application.ID | String | The unique identifier of the application associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Application.DisplayName | String | The display name of the application associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Application.TenantId | String | The tenant ID of the application identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ApplicationInstance.ID | String | The unique identifier of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ApplicationInstance.DisplayName | String | The display name of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ApplicationInstance.TenantId | String | The tenant ID of the application instance identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Conversation.ID | String | The unique identifier of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Conversation.DisplayName | String | The display name of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Conversation.TenantId | String | The tenant ID of the conversation identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ConversationIdentityType.ID | String | The identifier describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ConversationIdentityType.DisplayName | String | The display name describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.ConversationIdentityType.TenantId | String | The tenant ID of the conversation identity type. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Device.ID | String | The unique identifier of the device associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Device.DisplayName | String | The display name of the device associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Device.TenantId | String | The tenant ID of the device identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Encrypted.ID | String | The unique identifier of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Encrypted.DisplayName | String | The display name of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Encrypted.TenantId | String | The tenant ID of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.OnPremises.ID | String | The unique identifier of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.OnPremises.DisplayName | String | The display name of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.OnPremises.TenantId | String | The tenant ID of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Guest.ID | String | The unique identifier of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Guest.DisplayName | String | The display name of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Guest.TenantId | String | The tenant ID of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Phone.ID | String | The unique identifier of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Phone.DisplayName | String | The display name of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.Phone.TenantId | String | The tenant ID of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.User.ID | String | The unique identifier of the user associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.User.DisplayName | String | The display name of the user associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.CreatedBy.User.TenantId | String | The tenant ID of the user identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Application.ID | String | The unique identifier of the application associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Application.DisplayName | String | The display name of the application associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Application.TenantId | String | The tenant ID of the application identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ApplicationInstance.ID | String | The unique identifier of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ApplicationInstance.DisplayName | String | The display name of the application instance associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ApplicationInstance.TenantId | String | The tenant ID of the application instance identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Conversation.ID | String | The unique identifier of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Conversation.DisplayName | String | The display name of the conversation (team or channel) associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Conversation.TenantId | String | The tenant ID of the conversation identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ConversationIdentityType.ID | String | The identifier describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ConversationIdentityType.DisplayName | String | The display name describing whether the conversation identifies a team or channel. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.ConversationIdentityType.TenantId | String | The tenant ID of the conversation identity type. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Device.ID | String | The unique identifier of the device associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Device.DisplayName | String | The display name of the device associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Device.TenantId | String | The tenant ID of the device identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Encrypted.ID | String | The unique identifier of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Encrypted.DisplayName | String | The display name of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Encrypted.TenantId | String | The tenant ID of the encrypted identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.OnPremises.ID | String | The unique identifier of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.OnPremises.DisplayName | String | The display name of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.OnPremises.TenantId | String | The tenant ID of the on-premises identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Guest.ID | String | The unique identifier of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Guest.DisplayName | String | The display name of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Guest.TenantId | String | The tenant ID of the guest identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Phone.ID | String | The unique identifier of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Phone.DisplayName | String | The display name of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.Phone.TenantId | String | The tenant ID of the phone identity associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.User.ID | String | The unique identifier of the user associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.User.DisplayName | String | The display name of the user associated with this action. |
| MsGraph.eDiscoveryCase.HoldPolicy.LastModifiedBy.User.TenantId | String | The tenant ID of the user identity associated with this action. |
msg-update-ediscovery-case-hold-policy
Update the properties of an ediscoveryHoldPolicy object.
Base Command
msg-update-ediscovery-case-hold-policy
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the eDiscovery case. | Required |
| hold_policy_id | The policy ID that is intended to be updated. | Required |
| description | The description of the legal hold policy. | Optional |
| content_query | KQL query specifying content to be held in the specified locations. Learn about KQL (https://learn.microsoft.com/en-us/kusto/query/?view=azure-data-explorer&preserve-view=true). | Optional |
Context Output
There is no context output for this command.
<~PLATFORM>
License Requirements
The following configuration parameters require one of these licenses: Cortex XSIAM or Agentix:
- Fetch incidents
</~PLATFORM>
Configuration parameters
azure_cloud— Azure Cloudhost— Host URLcreds_auth_id—creds_tenant_id—creds_enc_key—auth_id— Application ID or Client IDtenant_id— Token or Tenant IDenc_key— Key or Client Secretcreds_certificate— Certificate Thumbprintcertificate_thumbprint— Certificate Thumbprintprivate_key— Private Keyredirect_uri— Application redirect URI (for self-deployed mode)auth_code— Authorization codeuse_managed_identities— Use Azure Managed Identitiesmanaged_identities_client_id—insecure— Trust any certificate (not secure)proxy— Use system proxy settingsself_deployed— Use a self-deployed Azure applicationisFetch— Fetch incidentsincidentFetchInterval— Incidents Fetch IntervalincidentType— Incident typefetch_incidents_type— Fetch incidents typefetch_time— First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)fetch_limit— Fetch limit per typefetch_service_sources— Alerts Service Sourcesfetch_filter— Alerts Filterfetch_incidents_filter— Incidents Filtermicrosoft_365_defender_context— Microsoft 365 Defender context
Commands (48)
-
ms-graph-security-auth-resetRun this command if for some reason you need to rerun the authentication process.
-
msg-activate-ediscovery-custodianActivate a custodian that has been released from a case to make them part of the case again. For details, see https://learn.microsoft.com/en-us/microsoft-365/compliance/ediscovery-manage-new-custodians?view=o365-worldwide#re-activate-custodian.
-
msg-advanced-huntingAdvanced hunting is a threat-hunting tool that uses specially constructed queries to examine the past 30 days of event data in Microsoft Graph Security.
-
msg-apply-hold-ediscovery-custodianStart the process of applying hold on eDiscovery custodians. Available return statuses: notApplied - The custodian is not on hold (all sources in it are not on hold). applied - The custodian is on hold (all sources are on hold). applying - The custodian is in applying the hold state (applyHold operation triggered). removing - The custodian is in removing the hold state(removeHold operation triggered). partial - The custodian is in a mixed state where some sources are on hold and some not on hold or error state.
-
msg-auth-testTests connectivity to Microsoft Graph Security.
-
msg-close-ediscovery-caseClose an eDiscovery case. When the legal case or investigation supported by a eDiscovery (Standard) case is completed, you can close the case. Here's what happens when you close a case: If the case contains any eDiscovery holds, they'll be turned off. After the hold is turned off, a 30-day grace period (called a delay hold) is applied to content locations that were on hold. This helps prevent content from being immediately deleted and provides admins the opportunity to search for and restore content before it may be permanently deleted after the delay hold period expires. For more information, see Removing content locations from an eDiscovery hold. Closing a case only turns off the holds that are associated with that case. If other holds are placed on a content location (such as a Litigation Hold, a retention policy, or a hold from a different eDiscovery (Standard) case) those holds will still be maintained. The case is still listed on the eDiscovery (Standard) page in the Microsoft Purview compliance portal. The details, holds, searches, and members of a closed case are retained. You can edit a case after it's closed. For example, you can add or remove members, create searches, and export search results. The primary difference between active and closed cases is that eDiscovery holds are turned off when a case is closed.
-
msg-create-alert-commentCreate a comment for an existing alert.
-
msg-create-ediscovery-caseCreate a new eDiscovery case. This command only creates an eDiscovery (Premium) case using the new case format. To learn more about the new case format in eDiscovery, see https://learn.microsoft.com/en-us/microsoft-365/compliance/advanced-ediscovery-new-case-format.
-
msg-create-ediscovery-case-hold-policyCreate a new ediscoveryHoldPolicy object.
-
msg-create-ediscovery-custodianCreate a new ediscoveryCustodian object. After the custodian object is created, you will need to create the custodian's userSource to reference their mailbox and OneDrive for Business site.
-
msg-create-ediscovery-custodian-site-sourceCreate a new siteSource object associated with an eDiscovery custodian. Use the msg-list-ediscovery-custodians command in order to get all available custodians.
-
msg-create-ediscovery-custodian-user-sourceCreate a new userSource object associated with an eDiscovery custodian. Use the msg-list-ediscovery-custodians command in order to get all available custodians.
-
msg-create-ediscovery-non-custodial-data-sourceCreate a new eDiscoveryNoncustodialDataSource object.
-
msg-create-ediscovery-searchCreate a new eDiscoverySearch object.
-
msg-create-email-file-assessment-requestCreate email file assessment request.
-
msg-create-file-assessment-requestCreate file assessment request.
-
msg-create-mail-assessment-requestCreate and retrieve a mail threat assessment.
-
msg-create-url-assessment-requestCreate the URL assessment request.
-
msg-delete-ediscovery-caseDelete an eDiscovery case. Before you can delete a case, you must first delete all holds listed on the holds page of the case. That includes deleting holds with a status of Off. Default hold policies can only be deleted when the hold is turned off. You must close an active case to turn off any default hold policies in the case. Once the holds are turned off for default hold policies, they can be deleted.
-
msg-delete-ediscovery-case-hold-policyDelete an eDiscovery hold policy. This command submits a deletion request to Microsoft Graph; the policy may not be removed immediately and can take some time to complete.
-
msg-delete-ediscovery-searchDelete an eDiscoverySearch object.
-
msg-export-result-ediscovery-dataExport results from an estimated ediscoverySearch. For details, see Manage a collection estimate (https://learn.microsoft.com/en-us/purview/ediscovery-create-draft-collection#manage-a-collection-estimate).
-
msg-generate-login-urlGenerate the login URL used for authorization code flow.
-
msg-get-alert-detailsGet details for a specific alert.
-
msg-get-last-estimate-statistics-operationRetrieves the most recent eDiscovery estimate statistics operation for a given search in Microsoft Purview. Use this command after running 'msg-run-estimate-statistics' to check the results.
-
msg-get-userDeprecatedDeprecated. Use the Entra ID Users integration instead.
-
msg-get-usersDeprecatedDeprecated. Use the Entra ID Users integration instead.
-
msg-list-case-operationGet a list of the caseOperation objects and their properties, or retrieve a specific operation by ID. When operation_id is provided, you can set download_file=true to download the export report (if available).
-
msg-list-ediscovery-case-hold-policyGet a list of the ediscoveryHoldPolicy objects and their properties.
-
msg-list-ediscovery-casesGet a list of the eDiscovery cases.
-
msg-list-ediscovery-custodian-site-sourcesGet a list of the siteSource objects associated with an eDiscoveryCustodian. Use the msg-list-ediscovery-custodians command in order to get all available custodians.
-
msg-list-ediscovery-custodian-user-sourcesGet a list of the userSource objects associated with an eDiscoveryCustodian. Use the msg-list-ediscovery-custodians command in order to get all available custodians.
-
msg-list-ediscovery-custodiansGet a list of the custodians on a given eDiscovery case.
-
msg-list-ediscovery-non-custodial-data-sourcesGet a list of the non-custodial data sources and their properties.
-
msg-list-ediscovery-searchsGet a list of eDiscoverySearch resources from an eDiscovery case.
-
msg-list-security-incidentGet a list of incident objects that Microsoft graph created to track attacks in an organization. If you want a specific incident, just enter incident_id.
-
msg-list-threat-assessment-requestsGet a list of the threat assessment requests.
-
msg-purge-ediscovery-dataDeletes mailbox items in Exchange or messages in Microsoft Teams that are included in an eDiscovery search. You can collect and purge the following categories of Teams content: Teams 1:1 chats - Chat messages, posts, and attachments shared in a Teams conversation between two people. Teams 1:1 chats are also called conversations. Teams group chats - Chat messages, posts, and attachments shared in a Teams conversation between three or more people. Also called 1:N chats or group conversations. Teams channels - Chat messages, posts, replies, and attachments shared in a standard Teams channel. Private channels - Message posts, replies, and attachments shared in a private Teams channel. Shared channels - Message posts, replies, and attachments shared in a shared Teams channel.
-
msg-release-ediscovery-custodianRelease a custodian from a case. For details, see https://learn.microsoft.com/en-us/microsoft-365/compliance/manage-new-custodians#release-a-custodian-from-a-case.
-
msg-remove-hold-ediscovery-custodianStart the process of removing a hold from eDiscovery custodians.
-
msg-reopen-ediscovery-caseReopen an eDiscovery case. When you reopen an eDiscovery (Premium) case, any holds that were in place when the case was closed won't be automatically reinstated. After the case is reopened, you'll have to go to the Holds tab and turn on the previous holds. To turn on a hold, select it to display the flyout page, and then set the Status toggle to On.
-
msg-run-estimate-statisticsStarts an eDiscovery estimate statistics operation in Microsoft Purview. The operation calculates the size and number of items matching the search query.
-
msg-search-alertsGet a list of alerts (security issues) within a customer's tenant that Microsoft or partner security solutions have identified.
-
msg-update-alertUpdate an editable alert property within any integrated solution to keep alert status and assignments in sync across solutions using its reference ID.
-
msg-update-ediscovery-caseUpdate an eDiscovery case.
-
msg-update-ediscovery-case-hold-policyUpdate the properties of an ediscoveryHoldPolicy object.
-
msg-update-ediscovery-searchUpdate an eDiscoverySearch object.
-
msg-update-security-incidentUpdate the incident with the given ID.
import re from enum import Enum from typing import Any import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 from MicrosoftApiModule import * # noqa: E402 from requests import Response from CommonServerUserPython import * # disable insecure warnings DEFAULT_KEYS_TO_REPLACE = {"createdDateTime": "CreatedDate"} APP_NAME = "ms-graph-security" CMD_URL = "security/alerts_v2" PAGE_SIZE_LIMIT = 2000 THREAT_ASSESSMENT_URL_PREFIX = "informationProtection/threatAssessmentRequests" MAX_ITEMS_PER_RESPONSE = 50 FETCH_INCIDENTS_TIMEOUT = 60 TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" # Maps Microsoft Graph Security severity to Cortex XSOAR severity (used for both fetched alerts and incidents). SEVERITY_MAP = { "low": IncidentSeverity.LOW, "medium": IncidentSeverity.MEDIUM, "high": IncidentSeverity.HIGH, "unknown": IncidentSeverity.UNKNOWN, "informational": IncidentSeverity.INFO, } DataSourceType = { "USER": { "type": "User", "url_suffix": "userSources", "unique_table_headers": ["IncludedSources"], "outputs_prefix": "CustodianUserSource", }, "SITE": {"type": "Site", "url_suffix": "siteSources", "unique_table_headers": [], "outputs_prefix": "CustodianSiteSource"}, "NON_CUSTODIAL": { "type": "Data", "url_suffix": "noncustodialDataSources", "unique_table_headers": ["LastModifiedDateTime", "ReleasedDateTime", "Status"], "outputs_prefix": "NoncustodialDataSource", }, } EMAIL_REGEX = r"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+" class HoldAction(Enum): APPLY = "apply" REMOVE = "remove" RELEVANT_DATA_TO_UPDATE = { "assigned_to": "assignedTo", "determination": "determination", "classification": "classification", "status": "status", } class MsGraphClient: """ Microsoft Graph Mail Client enables authorized access to a user's Office 365 mail data in a personal account. """ def __init__(self, tenant_id, proxy, certificate_thumbprint: str | None = None, **kwargs): self.ms_client = MicrosoftClient( tenant_id=tenant_id, proxy=proxy, certificate_thumbprint=certificate_thumbprint, managed_identities_resource_uri=Resources.graph, command_prefix=APP_NAME, **kwargs, ) def get(self, url, **kwargs): return self.ms_client.http_request(method="GET", url_suffix=url, **kwargs) def search_alerts(self, params): cmd_url = CMD_URL headers = {"Prefer": "include-unknown-enum-members"} # This header maps unknownFutureValue value to the appropriate service resource. # https://learn.microsoft.com/en-us/graph/api/resources/security-alert?view=graph-rest-1.0#:~:text=microsoftThreatIntelligence.%20Use%20the%20Prefer%3A-,include%2Dunknown%2Denum%2Dmembers,-request%20header%20to%20get%20the demisto.debug(f"Fetching MS Graph Security alerts with params: {params} and header: {headers}") response = self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params, headers=headers) return response def get_alert_details(self, alert_id): cmd_url = f"{CMD_URL}/{alert_id}" response = self.ms_client.http_request(method="GET", url_suffix=cmd_url) return response def update_alert(self, alert_id, params): cmd_url = f"{CMD_URL}/{alert_id}" self.ms_client.http_request(method="PATCH", url_suffix=cmd_url, json_data=params, resp_type="text") def get_users(self): cmd_url = "users" response = self.ms_client.http_request(method="GET", url_suffix=cmd_url) return response def get_user(self, user_id): cmd_url = f"users/{user_id}" response = self.ms_client.http_request(method="GET", url_suffix=cmd_url) return response def create_alert_comment(self, alert_id, params): cmd_url = f"{CMD_URL}/{alert_id}/comments" response = self.ms_client.http_request(method="POST", url_suffix=cmd_url, json_data=params) return response def list_ediscovery_cases(self, case_id: str | None): url = "security/cases/ediscoveryCases" if case_id: url += f"/{case_id}" return self.ms_client.http_request(method="GET", url_suffix=url) def create_edsicovery_case(self, display_name, description, external_id): url = "security/cases/ediscoveryCases" return self.ms_client.http_request( method="POST", url_suffix=url, json_data={"displayName": display_name, "description": description, "externalId": external_id}, ) def update_edsicovery_case(self, case_id, display_name, description, external_id): url = f"security/cases/ediscoveryCases/{case_id}" req = {"displayName": display_name, "description": description, "externalId": external_id} remove_nulls_from_dictionary(req) self.ms_client.http_request(ok_codes=[204], method="PATCH", url_suffix=url, json_data=req, resp_type="text") def close_edsicovery_case(self, case_id): url = f"security/cases/ediscoveryCases/{case_id}/close" self.ms_client.http_request(ok_codes=[204], method="POST", url_suffix=url, resp_type="text") def reopen_edsicovery_case(self, case_id): url = f"security/cases/ediscoveryCases/{case_id}/reopen" self.ms_client.http_request(ok_codes=[204], method="POST", url_suffix=url, resp_type="text") def release_edsicovery_custodian(self, case_id, custodian_id): url = f"security/cases/ediscoveryCases/{case_id}/custodians/{custodian_id}/release" self.ms_client.http_request(ok_codes=[202], method="POST", url_suffix=url, resp_type="text") def activate_edsicovery_custodian(self, case_id, custodian_id): url = f"security/cases/ediscoveryCases/{case_id}/custodians/{custodian_id}/activate" self.ms_client.http_request(ok_codes=[202], method="POST", url_suffix=url, resp_type="text") def delete_edsicovery_case(self, case_id): url = f"security/cases/ediscoveryCases/{case_id}" self.ms_client.http_request(ok_codes=[204], method="DELETE", url_suffix=url, resp_type="text") def create_edsicovery_custodian(self, case_id, email): url = f"security/cases/ediscoveryCases/{case_id}/custodians" return self.ms_client.http_request(method="POST", url_suffix=url, json_data={"email": email}) def list_ediscovery_custodians(self, case_id, custodian_id): url = f"security/cases/ediscoveryCases/{case_id}/custodians" if custodian_id: url += f"/{custodian_id}" return self.ms_client.http_request(method="GET", url_suffix=url) def create_edsicovery_custodian_user_source(self, case_id, custodian_id, email, included_sources): url = f"security/cases/ediscoveryCases/{case_id}/custodians/{custodian_id}/userSources" return self.ms_client.http_request( method="POST", url_suffix=url, json_data={"email": email, "includedSources": included_sources} ) def create_edsicovery_custodian_site_source(self, case_id, custodian_id, site): url = f"security/cases/ediscoveryCases/{case_id}/custodians/{custodian_id}/siteSources" return self.ms_client.http_request( method="POST", url_suffix=url, json_data={ "site": { "webUrl": site, } }, ) def list_ediscovery_custodians_sources(self, case_id, custodian_id, source_id, source_type): url = f'security/cases/ediscoveryCases/{case_id}/custodians/{custodian_id}/{source_type["url_suffix"]}' if source_id: url += f"/{source_id}" return self.ms_client.http_request(method="GET", url_suffix=url) def create_ediscovery_non_custodial_data_source(self, case_id, site, email): url = f"security/cases/ediscoveryCases/{case_id}/noncustodialDataSources" body = ( {"dataSource": {"@odata.type": "microsoft.graph.security.userSource", "email": email}} if email else {"dataSource": {"@odata.type": "microsoft.graph.security.siteSource", "site": {"webUrl": site}}} ) return self.ms_client.http_request(method="POST", url_suffix=url, json_data=body) def list_ediscovery_noncustodial_datasources(self, case_id, source_id): url = f"security/cases/ediscoveryCases/{case_id}/noncustodialDataSources" if source_id: url += f"/{source_id}" return self.ms_client.http_request(method="GET", url_suffix=url) def update_hold_ediscovery_custodian(self, case_id: str, custodian_id: str, hold_action: HoldAction): url = f"security/cases/ediscoveryCases/{case_id}/custodians/{hold_action.value}Hold" body = {"ids": custodian_id.split(",")} return self.ms_client.http_request(method="POST", url_suffix=url, resp_type="response", json_data=body) def create_ediscovery_search(self, case_id, display_name, description, query, data_source_scopes): url = f"/security/cases/ediscoveryCases/{case_id}/searches" body = { "displayName": display_name, "description": description, "contentQuery": query, "dataSourceScopes": data_source_scopes, } return self.ms_client.http_request(method="POST", url_suffix=url, json_data=body) def update_ediscovery_search(self, case_id, search_id, display_name, description, query, data_source_scopes): url = f"/security/cases/ediscoveryCases/{case_id}/searches/{search_id}" body = { "displayName": display_name, "description": description, "contentQuery": query, "dataSourceScopes": data_source_scopes, } remove_nulls_from_dictionary(body) self.ms_client.http_request(ok_codes=[204], method="PATCH", url_suffix=url, json_data=body, resp_type="text") def list_ediscovery_search(self, case_id, search_id): url = f"security/cases/ediscoveryCases/{case_id}/searches" if search_id: url += f"/{search_id}" return self.ms_client.http_request(method="GET", url_suffix=url) def delete_ediscovery_search(self, case_id, search_id): url = f"security/cases/ediscoveryCases/{case_id}/searches/{search_id}" self.ms_client.http_request(ok_codes=[204], method="DELETE", url_suffix=url, resp_type="text") def purge_ediscovery_data(self, case_id, search_id, purge_type, purge_areas): url = f"security/cases/ediscoveryCases/{case_id}/searches/{search_id}/purgeData" body = {"purgeType": purge_type, "purgeAreas": purge_areas} return self.ms_client.http_request(method="POST", url_suffix=url, json_data=body, resp_type="response") def start_estimate_statistics_request(self, case_id, search_id, statistics_options=None): url = f"security/cases/ediscoveryCases/{case_id}/searches/{search_id}/estimateStatistics" body = {} if statistics_options: # Handle lists or single values safely if isinstance(statistics_options, list): statistics_options = ",".join(statistics_options) body["statisticsOptions"] = statistics_options response = self.ms_client.http_request( method="POST", url_suffix=url, json_data=body, resp_type="response", ok_codes=[202], ) # Get the Location header which contains the location of the microsoft.graph.security.estimateStatisticsOperation # that was created to handle the estimate. location_url = response.headers.get("Location") if not location_url: raise DemistoException("Estimate statistics is not available for this search_id.") # Fetch operation status operation = self.ms_client.http_request(method="GET", full_url=location_url) return operation def get_last_estimate_statistics_operation(self, case_id: str, search_id: str): url = f"security/cases/ediscoveryCases/{case_id}/searches/{search_id}/lastEstimateStatisticsOperation" return self.ms_client.http_request(method="GET", url_suffix=url) def create_ediscovery_case_hold_policy( self, case_id: str, display_name: str, description: Any, content_query: Any, ) -> Any: """ Create a legal hold policy for an eDiscovery case. Args: case_id: The ID of the eDiscovery case. display_name: The display name of the hold policy. description: Description of the hold policy. content_query: KQL query defining content to be held. Returns: The created hold policy object. """ url = f"security/cases/ediscoveryCases/{case_id}/legalHolds" body = assign_params( displayName=display_name, description=description, contentQuery=content_query, ) return self.ms_client.http_request(method="POST", url_suffix=url, json_data=body) def delete_ediscovery_case_hold_policy( self, case_id: str, hold_policy_id: str, ) -> None: """ Delete a legal hold policy from an eDiscovery case. Args: case_id: The ID of the eDiscovery case. hold_policy_id: The ID of the hold policy to delete. Returns: None. """ url = f"security/cases/ediscoveryCases/{case_id}/legalHolds/{hold_policy_id}" self.ms_client.http_request( ok_codes=[204], method="DELETE", url_suffix=url, return_empty_response=True, ) def update_ediscovery_case_policy( self, case_id: str, hold_policy_id: str, description: Any, content_query: Any, ) -> None: """ Update an existing legal hold policy. Args: case_id: The ID of the eDiscovery case. hold_policy_id: The ID of the hold policy to update. description: Updated description. content_query: Updated content query. Returns: None. """ url = f"security/cases/ediscoveryCases/{case_id}/legalHolds/{hold_policy_id}" body = assign_params( description=description, contentQuery=content_query, ) self.ms_client.http_request( ok_codes=[204], method="PATCH", url_suffix=url, json_data=body, return_empty_response=True, ) def list_ediscovery_case_hold_policy( self, case_id: str, limit: int | None, ) -> Any: """ List legal hold policies for an eDiscovery case. Args: case_id: The ID of the eDiscovery case. limit: Maximum number of results to return. Returns: A list of hold policy objects. """ url = f"security/cases/ediscoveryCases/{case_id}/legalHolds" if limit: url += f"?$top={limit}" return self.ms_client.http_request(ok_codes=[200], method="GET", url_suffix=url) def get_ediscovery_case_hold_policy( self, case_id: str, hold_policy_id: str, ) -> Any: """ Retrieve a specific legal hold policy. Args: case_id: The ID of the eDiscovery case. hold_policy_id: The ID of the hold policy. Returns: The hold policy object. """ url = f"security/cases/ediscoveryCases/{case_id}/legalHolds/{hold_policy_id}" return self.ms_client.http_request(ok_codes=[200], method="GET", url_suffix=url) def list_case_operation( self, case_id: str, limit: int | None, ) -> Any: """ List operations for an eDiscovery case. Args: case_id: The ID of the eDiscovery case. limit: Maximum number of results to return. Returns: A list of case operation objects. """ url = f"security/cases/ediscoveryCases/{case_id}/operations" if limit: url += f"?$top={limit}" return self.ms_client.http_request(ok_codes=[200], method="GET", url_suffix=url) def get_case_operation( self, case_id: str, operation_id: str, ) -> Any: """ Retrieve a specific eDiscovery case operation. Args: case_id: The ID of the eDiscovery case. operation_id: The ID of the operation. Returns: The operation object. """ url = f"security/cases/ediscoveryCases/{case_id}/operations/{operation_id}" return self.ms_client.http_request(ok_codes=[200], method="GET", url_suffix=url) def export_result_ediscovery_data( self, case_id: str, search_id: str, additional_options: str, export_criteria: str, export_format: str, cloud_attachment_version: str, description: str, display_name: str, document_version: str, export_location: str, ) -> Any: """ Export search results from an eDiscovery case. Args: case_id: The ID of the eDiscovery case. search_id: The ID of the eDiscovery search. additional_options: Additional export options. export_criteria: Criteria defining what to export. export_format: Export format. cloud_attachment_version: Cloud attachment version. description: Export description. display_name: Display name of the export. document_version: Document version. export_location: Export destination. Returns: HTTP response object for the export request. """ url = f"security/cases/ediscoveryCases/{case_id}/searches/{search_id}/exportResult" body = assign_params( additionalOptions=additional_options, exportCriteria=export_criteria, exportFormat=export_format, cloudAttachmentVersion=cloud_attachment_version, description=description, displayName=display_name, documentVersion=document_version, exportLocation=export_location, ) headers = {"Prefer": "include-unknown-enum-members"} return self.ms_client.http_request( method="POST", url_suffix=url, json_data=body, headers=headers, ok_codes=[202], return_empty_response=True, resp_type="response", ) def create_mail_assessment_request(self, recipient_email, expected_assessment, category, user_id, message_id): body = { "@odata.type": "#microsoft.graph.mailAssessmentRequest", "recipientEmail": recipient_email, "expectedAssessment": expected_assessment, "category": category, "messageUri": urljoin(self.ms_client._base_url, "users/{user_id}/messages/{message_id}"), } return self.ms_client.http_request(method="POST", url_suffix=THREAT_ASSESSMENT_URL_PREFIX, json_data=body) def get_user_id(self, email): return self.ms_client.http_request(method="GET", url_suffix="users", params={"$filter": f"mail eq '{email}'"}) def get_threat_assessment_request(self, request_id): return self.ms_client.http_request( method="GET", url_suffix=f"{THREAT_ASSESSMENT_URL_PREFIX}/{request_id}", params={"$expand": "results"} ) def get_threat_assessment_request_status(self, request_id): return self.ms_client.http_request( method="GET", url_suffix=f"{THREAT_ASSESSMENT_URL_PREFIX}/{request_id}", params={"$select": "status"} ) def create_email_file_assessment_request(self, recipient_email, expected_assessment, category, content_data): body = { "@odata.type": "#microsoft.graph.emailFileAssessmentRequest", "recipientEmail": recipient_email, "expectedAssessment": expected_assessment, "category": category, "contentData": content_data, } return self.ms_client.http_request(method="POST", url_suffix=THREAT_ASSESSMENT_URL_PREFIX, json_data=body) def create_file_assessment_request(self, expected_assessment, category, file_name, content_data): body = { "@odata.type": "#microsoft.graph.fileAssessmentRequest", "expectedAssessment": expected_assessment, "category": category, "fileName": file_name, "contentData": content_data, } return self.ms_client.http_request(method="POST", url_suffix=THREAT_ASSESSMENT_URL_PREFIX, json_data=body) def create_url_assessment_request(self, expected_assessment, category, url): body = { "@odata.type": "#microsoft.graph.urlAssessmentRequest", "expectedAssessment": expected_assessment, "category": category, "url": url, } return self.ms_client.http_request(method="POST", url_suffix=THREAT_ASSESSMENT_URL_PREFIX, json_data=body) def list_threat_assessment_requests(self, filters=None, order_by=None, sort_order=None, next_token=None): params = {} if next_token: return self.ms_client.http_request( method="GET", url_suffix=THREAT_ASSESSMENT_URL_PREFIX, params={"$skipToken": next_token}, ) if filters: params["$filter"] = filters if order_by: params["$orderby"] = order_by if sort_order: params["$orderby"] = f"{order_by} {sort_order}" return self.ms_client.http_request(method="GET", url_suffix=THREAT_ASSESSMENT_URL_PREFIX, params=params) def advanced_hunting_request(self, query: str, timeout: int): """ POST request to the advanced hunting API: Args: query (str): query advanced hunting query language timeout (int): The amount of time (in seconds) that a request will wait for a client to establish a connection to a remote machine before a timeout occurs. Returns: The response object contains three top-level properties: Stats - A dictionary of query performance statistics. Schema - The schema of the response, a list of Name-Type pairs for each column. Results - A list of advanced hunting events. """ return self.ms_client.http_request( method="POST", url_suffix="security/runHuntingQuery", json_data={"Query": query}, timeout=timeout ) def get_incidents_request( self, url_suffix: str, timeout: int, headers: dict | None = None, ) -> dict: """ Perform a GET request to retrieve incidents. Args: url_suffix (str): The URL suffix for the request, including any filters or additional parameters. timeout (int): The timeout for the request in seconds. headers (dict | None): Optional request headers to send with the request. Returns: dict: The request results as a dictionary, containing: - '@odata.context' - 'value': The updated incident(s). """ incident = self.ms_client.http_request(method="GET", url_suffix=url_suffix, timeout=timeout, headers=headers) return incident def update_incident_request( self, incident_id: int, status: Optional[str], assigned_to: Optional[str], classification: Optional[str], determination: Optional[str], severity: Optional[str], resolving_comment: Optional[str], custom_tags: Optional[List[str]], timeout: int, ) -> dict: """ PATCH request to update single incident. Args: incident_id (int): incident's id status (str): Specifies the current status of the alert. Possible values are: (Active, Resolved or Redirected) assigned_to (str): Owner of the incident. classification (str): Specification of the alert. Possible values are: Unknown, FalsePositive, TruePositive. determination (str): Specifies the determination of the alert. Possible values are: NotAvailable, Apt, Malware, SecurityPersonnel, SecurityTesting, UnwantedSoftware, Other. severity (str): Indicates the possible impact on assets. The higher the severity, the bigger the impact. Typically, higher severity items require the most immediate attention. The possible values are: unknown, informational, low, medium, high, unknownFutureValue. resolving_comment (str): User input that explains the resolution of the incident and the classification choice. It contains free editable text. tags (list): Custom tags associated with an incident. Separated by commas without spaces (CSV) for example: tag1,tag2,tag3. timeout (int): The amount of time (in seconds) that a request will wait for a client to establish a connection to a remote machine before a timeout occurs. comment (str): Comment to be added to the incident Returns( Dict): request results as dict: { '@odata.context', 'value': updated incident, } """ body = assign_params( status=status, assignedTo=assigned_to, classification=classification, determination=determination, severity=severity, resolvingComment=resolving_comment, customTags=custom_tags, ) updated_incident = self.ms_client.http_request( method="PATCH", url_suffix=f"security/incidents/{incident_id}", json_data=body, timeout=timeout ) return updated_incident def download_export_file(self, download_url: str): """ Download an eDiscovery export file using the download URL returned by Microsoft Graph. Args: download_url (str): The pre-authorized download URL returned in the `exportFileMetadata` property of an export operation. Returns: requests.Response: The raw HTTP response object (resp_type="response"), which can be streamed or saved to disk by the caller. """ response = self.ms_client.http_request( method="GET", headers={"X-AllowWithAADToken": "true"}, full_url=download_url, resp_type="response", scope="b26e684c-5068-4120-a679-64a5d2c909d9/.default", ) # Force refresh token reset to default scope and avoid token-scope drift. self.ms_client.get_access_token(scope=self.ms_client.scope) return response """ HELPER FUNCTIONS """ def get_status_of_operation(client: MsGraphClient, res: Response) -> str: """ Some responses from MSG where an action is called return a url in the headers that we can use to retrieve the status Args: client: Microsoft GraphClient res: the response from the api Returns: The status """ location = res.headers.get("Location") status = "success" # if no location is returned then the custodian is already in this state/theres no data sources if location: location = "security" + location.split("/security")[1] # chop off the baseurl resp = client.get(location) demisto.debug(f"response from location get: {resp}") status = resp.get("status") return status def create_search_alerts_filters(args, is_fetch=False): """ Creates the relevant filters for the search_alerts function. Args: args (Dict): The command's arguments dictionary. is_fetch (bool): whether the search_alerts function is being called from fetch alerts or not. Returns: Dict: The filter dictionary to use """ last_modified = args.get("last_modified") severity = args.get("severity") category = args.get("category") time_from = args.get("time_from") time_to = args.get("time_to") filter_query = args.get("filter") page = args.get("page") page_size = int(args.get("page_size", 50)) if (is_fetch and args.get("page_size")) or not is_fetch else 0 filters = [] params: dict[str, str] = {} if last_modified: filters.append(f"lastUpdateDateTime gt {get_timestamp(last_modified)}") if category: filters.append(f"category eq '{category}'") if severity: filters.append(f"severity eq '{severity}'") if time_from: # changed to ge and le in order to solve issue #27884 filters.append(f"createdDateTime ge {time_from}") if time_to: filters.append(f"createdDateTime le {time_to}") if filter_query: # Wrap in parentheses so an `or` clause can't escape the createdDateTime time window (OData `and` binds before `or`). filters.append(f"({filter_query})") if page_size: if page_size > PAGE_SIZE_LIMIT: raise DemistoException(f"Please note that the page size limit is {PAGE_SIZE_LIMIT}") params["$top"] = str(page_size) if page and page_size: page = int(page) page = page * page_size params["$skip"] = page relevant_filters_v2 = ["classification", "serviceSource", "status"] for key in relevant_filters_v2: if val := args.get(key): filters.append(f"{key} eq '{val}'") filters = " and ".join(filters) params["$filter"] = filters return params def created_by_fields_to_hr(ret_context: dict): hr = ret_context.copy() hr["CreatedByName"] = dict_safe_get(ret_context, ["CreatedBy", "User", "DisplayName"]) hr["CreatedByUPN"] = dict_safe_get(ret_context, ["CreatedBy", "User", "UserPrincipalName"]) hr["CreatedByAppName"] = dict_safe_get(ret_context, ["CreatedBy", "Application", "DisplayName"]) hr.pop("CreatedBy", None) return hr def create_data_to_update(args): """ Creates the data dictionary to update alert for the update_alert function. Args: args (Dict): The command's arguments dictionary. Returns: Dict: A dictionary object containing the alert's fields to update. """ if all(not args.get(key) for key in list(RELEVANT_DATA_TO_UPDATE.keys())): raise DemistoException( f"No data to update was provided, please provide at least one of the" f" following: {(', ').join(list(RELEVANT_DATA_TO_UPDATE.keys()))}." ) data: dict[str, Any] = {} for relevant_args_key, relevant_data_key in RELEVANT_DATA_TO_UPDATE.items(): if val := args.get(relevant_args_key): data[relevant_data_key] = val return data def get_timestamp(time_description): if time_description == "Last24Hours": time_delta = 1 elif time_description == "Last48Hours": time_delta = 2 else: time_delta = 7 return datetime.strftime(datetime.now() - timedelta(time_delta), "%Y-%m-%d") def capitalize_dict_keys_first_letter(response, keys_to_replace: dict = DEFAULT_KEYS_TO_REPLACE): """ Recursively creates a data dictionary where all key starts with capital letters. Args: keys_to_replace: keys that should have custom replacements not according to capitalize_first_letter response (Dict / str): The dictionary to update. Returns: Dict: The updated dictionary. """ if isinstance(response, str): return response parsed_dict: dict = {} if isinstance(response, dict): for key, value in response.items(): if keys_to_replace and key in keys_to_replace: parsed_dict[keys_to_replace[key]] = value elif key == "id": parsed_dict["ID"] = value elif isinstance(value, dict): parsed_dict[capitalize_first_letter(key)] = capitalize_dict_keys_first_letter(value) elif isinstance(value, list): parsed_dict[capitalize_first_letter(key)] = [capitalize_dict_keys_first_letter(list_item) for list_item in value] else: parsed_dict[capitalize_first_letter(key)] = value return parsed_dict def capitalize_first_letter(string): return string[:1].upper() + string[1:] def list_ediscovery_custodian_sources(client: MsGraphClient, args, source_type): raw_res = client.list_ediscovery_custodians_sources( args.get("case_id"), args.get("custodian_id"), args.get(f"{source_type['type']}_source_id".lower()), source_type ) if source_list := raw_res.get("value"): demisto.info(f"returned {len(source_list)} results from the api") else: source_list = [raw_res] # api doesnt return a list if only 1 result if not argToBoolean(args.get("all_results", "false")): source_list = source_list[: arg_to_number(args.get("limit", 50))] return ediscovery_source_command_results(source_list, source_type, raw_res) def create_filter_query(filter_param: str, service_sources_param: str): """ Creates the relevant filters to the query filter according to the user's configured filter. Args: filter_param (str): configured user filter. service_sources_param (str): comma separated list of service_sources to fetch alerts by. Returns: str: filter query to use """ filter_query = "" if filter_param: filter_query = filter_param elif service_sources_param: demisto.debug("Using service sources param for filter") service_sources_lst = [source.strip() for source in service_sources_param.split(",")] # This creates a string like: "serviceSource in ('source1','source2')" # see docs supporting this operation: https://learn.microsoft.com/en-us/graph/filter-query-parameter?tabs=http quoted_sources = [f"'{source}'" for source in service_sources_lst] filter_query = f"serviceSource in ({','.join(quoted_sources)})" demisto.debug("filter query: " + str(filter_query)) return filter_query def to_cases_hr(ret_context: dict): hr = ret_context.copy() hr["LastModifiedByName"] = dict_safe_get(ret_context, ["LastModifiedBy", "User", "DisplayName"]) hr["ClosedByName"] = dict_safe_get(ret_context, ["ClosedBy", "User", "DisplayName"]) return hr def ediscovery_cases_command_results(raw_case_list: list, raw_res=None) -> CommandResults: """ Returns the CommandResults for a list of eDiscoveryCases from the API Args: raw_res: the raw_response to be used. If not provided assumed response==raw_res limit: max number of entries to return. Does not affect raw_result raw_case_list: the raw response from the api, as a list Returns: A CommandResults object """ return to_msg_command_results( raw_object_list=raw_case_list, raw_res=raw_res, outputs_prefix="MsGraph.eDiscoveryCase", output_key_field="CaseId", raw_keys_to_replace={"status": "CaseStatus", "id": "CaseId"}, table_headers=[ "DisplayName", "Description", "ExternalId", "CaseStatus", "CaseId", "CreatedDateTime", "LastModifiedDateTime", "LastModifiedByName", "ClosedByName", ], to_hr=to_cases_hr, ) def custodian_to_hr(ret_context: dict): hr = ret_context.copy() hr["LastModifiedByName"] = dict_safe_get(ret_context, ["LastModifiedBy", "User", "DisplayName"]) hr["ClosedByName"] = dict_safe_get(ret_context, ["ClosedBy", "User", "DisplayName"]) return hr def ediscovery_custodian_command_results(raw_custodian_list, raw_res=None): return to_msg_command_results( raw_object_list=raw_custodian_list, raw_res=raw_res, outputs_prefix="MsGraph.eDiscoveryCustodian", output_key_field="CustodianId", raw_keys_to_replace={"status": "CustodianStatus", "id": "CustodianId"}, table_headers=[ "DisplayName", "Email", "CustodianStatus", "CustodianId", "CreatedDateTime", "LastModifiedDateTime", "LastModifiedByName", "ClosedByName", "AcknowledgedDateTime", "HoldStatus", "ReleasedDateTime", ], to_hr=custodian_to_hr, ) def to_msg_command_results( raw_object_list, outputs_prefix, output_key_field, raw_keys_to_replace, raw_res=None, table_headers=[], to_hr=lambda x: x ): """ General function to return command results for Microsoft Graph. Keys beginning with @ will be stripped from the response. Keys will be converted to CapitalCaseFormat Empty elements will be removed Args: raw_object_list: the list of objects from the api. One item will be converted to a list of one outputs_prefix: The outputs prefix for the command output_key_field: The key field for the CommandResults raw_keys_to_replace: Keys to replace with a specific alternative. EG { 'id' : 'CustomID' } raw_res: The raw response exactly as received from the API. Will assume same as raw_object_list if not provided table_headers: Headers to show in the human readable output to_hr: A function that will take a context dictionary as input and convert it to a human readable dictionary. Default is identity function Returns: A CommandResults object """ raw_res = raw_res or raw_object_list if not isinstance(raw_object_list, list): raw_object_list = [raw_object_list] context_list = [] human_readable_list = [] for res in raw_object_list: context = capitalize_dict_keys_first_letter(res, keys_to_replace=raw_keys_to_replace) keys_to_del = [key for key in context if key.startswith("@")] for key in keys_to_del: del context[key] context = remove_empty_elements(context) context_list.append(context) human_readable_list.append(to_hr(context)) return CommandResults( outputs_prefix=outputs_prefix, outputs_key_field=output_key_field, raw_response=raw_res, outputs=context_list, readable_output=tableToMarkdown( "Results:", human_readable_list, headers=table_headers, headerTransform=pascalToSpace, removeNull=True ), ) def to_ediscovery_search_command_results(resp, raw_res=None): return to_msg_command_results( resp, raw_res=raw_res, output_key_field="SearchId", outputs_prefix="MsGraph.eDiscoverySearch", raw_keys_to_replace={"id": "SearchId"}, table_headers=[ "DisplayName", "Description", "DataSourceScopes", "SearchId", "CreatedByName", "CreatedByAppName", "CreatedByUPN", "CreatedDateTime", "LastModifiedDateTime", "AdditionalSources", ], to_hr=created_by_fields_to_hr, ) def ediscovery_source_command_results(raw_case_list: list, source_type, raw_res=None): type_name = source_type["type"] demisto.debug(f"Returning command results for source {type_name}") output_key_field = f"{type_name}SourceId" return to_msg_command_results( raw_object_list=raw_case_list, raw_res=raw_res, outputs_prefix=f'MsGraph.{source_type["outputs_prefix"]}', output_key_field=output_key_field, raw_keys_to_replace={"id": output_key_field}, table_headers=[ "DisplayName", "Email", output_key_field, "HoldStatus", "CreatedDateTime", "CreatedByName", "CreatedByUPN", "CreatedByAppName", "SiteWebUrl", ] + source_type["unique_table_headers"], to_hr=created_by_fields_to_hr, ) def query_set_limit(query: str, limit: int) -> str: """ Set the limit of a query if it doesn't already have a limit set. Args: query (str): The query string. limit (int): The limit to set. Returns: str: The modified query string with the limit set, or the original query if it already had a limit set or if the limit is less than 0. """ if limit < 0 or "limit " in query or "take " in query: return query return f"{query} | limit {limit}" def convert_list_incidents_to_readable(incidents_list: list) -> list: """ Convert a list of raw incidents to a list of readable incidents. Args: incidents_list (list): The list of raw incidents to convert, expected to be in the format [incident1, incident2, ...]. Returns: list: A list containing the converted readable incidents. """ readable_incidents = [] for incident in incidents_list: readable_incident = convert_single_incident_to_readable(incident) readable_incidents.append(readable_incident) return readable_incidents def convert_single_incident_to_readable(raw_incident: dict) -> dict: """ Converts incident received from Microsoft Graph Security to readable format Args: raw_incident (Dict): The incident as received from Microsoft Graph Security Returns: new dictionary with keys mapping. """ if not raw_incident: raw_incident = {} return { "Display name": raw_incident.get("displayName"), "id": raw_incident.get("id"), "Severity": raw_incident.get("severity"), "Status": raw_incident.get("status"), "Assigned to": raw_incident.get("assignedTo", "Unassigned"), "Custom tags": ", ".join(raw_incident.get("customTags", [])), "System tags": ", ".join(raw_incident.get("systemTags", [])), "Classification": raw_incident.get("classification"), "Determination": raw_incident.get("determination"), "Created date time": raw_incident.get("createdDateTime"), "Updated date time": raw_incident.get("lastUpdateDateTime"), } def get_list_incidents(client: MsGraphClient, args: dict) -> list: """ Retrieve a list of security incidents based on the provided arguments. Args: client (MsGraphClient): The Microsoft Graph client instance. args (dict): A dictionary containing the command arguments Returns: list: A list of security incidents. """ timeout = arg_to_number(args["timeout"]) # default value is defined limit = arg_to_number(args["limit"]) # default value is defined url_suffix = set_url_suffix_list_incidents(args) # type:ignore[arg-type] incidents_response = client.get_incidents_request(url_suffix, timeout) # type:ignore[arg-type] incidents_list: list = incidents_response.get("value", []) count_incidents = len(incidents_list) nextLink = incidents_response.get("@odata.nextLink") while nextLink and count_incidents < limit: # type:ignore[operator] url_suffix = f'security/{nextLink.split("/")[-1]}' top = limit - count_incidents # type:ignore[operator] if top <= MAX_ITEMS_PER_RESPONSE: url_suffix += f"&$top={top}" new_incidents_respond = client.get_incidents_request(url_suffix, timeout) # type:ignore[arg-type] incidents_list.extend(new_incidents_respond.get("value", [])) count_incidents = len(incidents_list) nextLink = new_incidents_respond.get("@odata.nextLink") return incidents_list def set_url_suffix_list_incidents(args: dict) -> str: """ Set the URL suffix for retrieving a list of security incidents based on the provided arguments. Args: args (dict): A dictionary containing the command arguments: - 'limit' (str): The maximum number of incidents to retrieve. - 'status' (str): Filter by status. - 'assigned_to' (str): Filter by assigned user. - 'severity' (str): Filter by severity. - 'classification' (str): Filter by classification. - 'odata' (str): Filter by odata. - 'extra_data' (bool): Whether to include each incident's related alerts. Returns: str: The URL suffix for the request. """ limit = arg_to_number(args["limit"]) # default value is defined top = limit if limit <= MAX_ITEMS_PER_RESPONSE else None # type:ignore[operator] # Typed args are wrapped as "{property} eq '{value}'" clauses. args_for_filter = { "status": args.get("status"), "assigned_to": args.get("assigned_to"), "severity": args.get("severity"), "classification": args.get("classification"), } # The "odata" arg is a raw OData $filter expression and is appended as-is (not wrapped). odata = args.get("odata") filters = [] url_suffix = "security/incidents?" if argToBoolean(args.get("extra_data", False)): # Include each incident's related alerts as part of the response. url_suffix += "$expand=alerts&" if top: url_suffix += f"$top={top!s}" if any(args_for_filter.values()) or odata: url_suffix += "&$filter=" for key, value in args_for_filter.items(): if value: filters.append(f"{key} eq '{value}'") if odata: filters.append(odata) url_suffix += " and ".join(filters) return url_suffix """ COMMAND FUNCTIONS """ def fetch_incidents_and_alerts(client: MsGraphClient, params: dict) -> list: """ Fetches Alerts and/or Incidents, based on the "Fetch incidents type" parameter. Each type has its own last run time, so fetching both does not affect one another. Args: client (MsGraphClient): MsGraphClient client object. params (dict): the integration parameters. Returns: list: all the fetched items (alerts and/or incidents) together. """ fetch_time = params.get("fetch_time", "3 days") fetch_limit = params.get("fetch_limit", MAX_ITEMS_PER_RESPONSE) or MAX_ITEMS_PER_RESPONSE fetch_service_sources = params.get("fetch_service_sources", "") fetch_alerts_filter = params.get("fetch_filter", "") fetch_incidents_filter = params.get("fetch_incidents_filter", "") fetch_incidents_type = argToList(params.get("fetch_incidents_type")) last_run = demisto.getLastRun() or {} # Migrate the old flat last_run format ({"time": "..."}) to the new nested format, # so upgraded instances don't re-fetch the entire window and create duplicate incidents. if "time" in last_run and "alerts_last_run" not in last_run and "incidents_last_run" not in last_run: demisto.debug("Migrating old last_run format to the new nested format.") old_time = {"time": last_run["time"]} last_run = {"alerts_last_run": old_time, "incidents_last_run": old_time} new_last_run: dict = dict(last_run) fetched: list = [] demisto.debug(f"Starting fetch. Types: {fetch_incidents_type}. Limit per type: {fetch_limit}.") if "Alerts" in fetch_incidents_type: alerts, alerts_last_run = fetch_alerts( client, fetch_time=fetch_time, fetch_limit=int(fetch_limit), extra_filter=fetch_alerts_filter, service_sources=fetch_service_sources, last_run=last_run.get("alerts_last_run", {}), ) demisto.debug(f"Fetched {len(alerts)} alerts. New alerts last run: {alerts_last_run}.") fetched.extend(alerts) new_last_run["alerts_last_run"] = alerts_last_run if "Incidents" in fetch_incidents_type: incidents, incidents_last_run = fetch_incidents( client, fetch_time=fetch_time, fetch_limit=int(fetch_limit), extra_filter=fetch_incidents_filter, last_run=last_run.get("incidents_last_run", {}), ) demisto.debug(f"Fetched {len(incidents)} incidents. New incidents last run: {incidents_last_run}.") fetched.extend(incidents) new_last_run["incidents_last_run"] = incidents_last_run demisto.setLastRun(new_last_run) return fetched def fetch_incidents( client: MsGraphClient, fetch_time: str, fetch_limit: int, extra_filter: str, last_run: dict ) -> tuple[list, dict]: """ Fetches up to `fetch_limit` incidents created within the fetch time window. Each fetched incident includes its related alerts as raw data. Args: client (MsGraphClient): MsGraphClient client object. fetch_time (str): how far back to fetch on the first run (e.g. "1 day"). fetch_limit (int): the maximum number of incidents to fetch. extra_filter (str): an optional extra filter to add to the time window. last_run (dict): the incidents last run from the previous fetch. Returns: tuple[list, dict]: the fetched incidents, and the updated incidents last run. """ # Copy the input last_run so we never mutate the caller's argument. new_last_run = dict(last_run) if last_run else {"time": parse_date_range(fetch_time, date_format=TIMESTAMP_FORMAT)[0]} demisto_incidents: list = [] time_from = new_last_run.get("time") time_to = datetime.now().strftime(TIMESTAMP_FORMAT) # Fetch incidents within the time window (plus the optional user filter), and include their alerts. # $top is set to fetch_limit so we request up to fetch_limit incidents in a single request. filter_expression = f"createdDateTime gt {time_from} and createdDateTime le {time_to}" if extra_filter: # Wrap in parentheses so an `or` clause can't escape the createdDateTime time window (OData `and` binds before `or`). filter_expression += f" and ({extra_filter})" url_suffix = f"security/incidents?$expand=alerts&$top={fetch_limit}&$filter={filter_expression}&$orderby=createdDateTime asc" # This header maps unknownFutureValue enum values to the appropriate real value (e.g. new service sources). headers = {"Prefer": "include-unknown-enum-members"} demisto.debug(f"Fetching MS Graph Security incidents. From: {time_from}. To: {time_to}.") incidents = client.get_incidents_request(url_suffix, FETCH_INCIDENTS_TIMEOUT, headers=headers).get("value", []) if incidents: count = 0 incidents = sorted(incidents, key=lambda k: k["createdDateTime"]) # sort the incidents by time-increasing order last_incident_time = last_run.get("time", "0") demisto.debug(f'Incidents times: {[incidents[i]["createdDateTime"] for i in range(len(incidents))]}\n') for incident in incidents: incident_time = incident.get("createdDateTime") if incident_time > last_incident_time and count < fetch_limit: demisto_incidents.append( { "name": f'{incident.get("displayName")} - {incident.get("id")}', "occurred": incident.get("createdDateTime"), "severity": SEVERITY_MAP.get(incident.get("severity", ""), 0), "rawJSON": json.dumps(incident), } ) count += 1 if demisto_incidents: last_incident_time = demisto_incidents[-1].get("occurred") new_last_run.update({"time": last_incident_time}) return demisto_incidents, new_last_run def fetch_alerts( client: MsGraphClient, fetch_time: str, fetch_limit: int, extra_filter: str, service_sources: str, last_run: dict ) -> tuple[list, dict]: """ Fetches up to `fetch_limit` alerts created within the fetch time window, matching the given filters. Args: client (MsGraphClient): MsGraphClient client object. fetch_time (str): how far back to fetch on the first run (e.g. "1 day"). fetch_limit (int): the maximum number of alerts to fetch. extra_filter (str): an optional user filter. service_sources (str): a comma separated list of service sources to fetch alerts by. last_run (dict): the alerts last run from the previous fetch. Returns: tuple[list, dict]: the fetched alerts, and the updated alerts last run. """ filter_query = create_filter_query(extra_filter, service_sources) # Copy the input last_run so we never mutate the caller's argument. new_last_run = dict(last_run) if last_run else {"time": parse_date_range(fetch_time, date_format=TIMESTAMP_FORMAT)[0]} demisto_alerts: list = [] time_from = new_last_run.get("time") time_to = datetime.now().strftime(TIMESTAMP_FORMAT) # Get alerts from MS Graph Security. Pass fetch_limit as the page size so we request up to fetch_limit alerts. demisto.debug(f"Fetching MS Graph Security alerts. From: {time_from}. To: {time_to}. Filter: {filter_query}") args = {"time_to": time_to, "time_from": time_from, "filter": filter_query, "page_size": fetch_limit} params = create_search_alerts_filters(args, is_fetch=True) alerts = client.search_alerts(params)["value"] if alerts: count = 0 alerts = sorted(alerts, key=lambda k: k["createdDateTime"]) # sort the alerts by time-increasing order last_alert_time = last_run.get("time", "0") demisto.debug(f'Alerts times: {[alerts[i]["createdDateTime"] for i in range(len(alerts))]}\n') for alert in alerts: alert_time = alert.get("createdDateTime") if alert_time > last_alert_time and count < fetch_limit: demisto_alerts.append( { "name": f'{alert.get("title", "Unknown")} - {alert.get("id", "Unknown")}', "occurred": alert.get("createdDateTime"), "severity": SEVERITY_MAP.get(alert.get("severity", ""), 0), "rawJSON": json.dumps(alert), } ) count += 1 if demisto_alerts: last_alert_time = demisto_alerts[-1].get("occurred") new_last_run.update({"time": last_alert_time}) return demisto_alerts, new_last_run def search_alerts_command(client: MsGraphClient, args): """ Retrieve a list of alerts filtered by the given filter arguments. Args: client (MsGraphClient): MsGraphClient client object. args (Dict): The command's arguments dictionary. Returns: str, Dict, Dict: table of returned alerts, parsed outputs and request's response. """ params = create_search_alerts_filters(args, is_fetch=False) alerts = client.search_alerts(params)["value"] limit = int(args.get("limit")) if limit < len(alerts): alerts = alerts[:limit] outputs = [capitalize_dict_keys_first_letter(alert) for alert in alerts] table_headers = [ "ID", "DetectionSource", "ServiceSource", "Title", "Category", "Severity", "CreatedDate", "LastUpdateDateTime", "Status", "IncidentId", ] ec = {"MsGraph.Alert(val.ID && val.ID === obj.ID)": outputs} human_readable = tableToMarkdown("Microsoft Security Graph Alerts", outputs, table_headers, removeNull=True) return human_readable, ec, alerts def get_alert_details_command(client: MsGraphClient, args): """ Retrieve information about an alert with the given id. Args: client (MsGraphClient): MsGraphClient client object. args (Dict): The command's arguments dictionary. Returns: str, Dict, Dict: Human readable output with information about the alert, parsed outputs and request's response. """ alert_id = args.get("alert_id") alert_details = client.get_alert_details(alert_id) hr = f"## Microsoft Security Graph Alert Details - {alert_id}\n" outputs = capitalize_dict_keys_first_letter(alert_details) table_headers = [ "ID", "DetectionSource", "ServiceSource", "Title", "Category", "Severity", "CreatedDate", "LastUpdateDateTime", "Status", "IncidentId", ] ec = {"MsGraph.Alert(val.ID && val.ID === obj.ID)": outputs} hr += tableToMarkdown("", outputs, table_headers, removeNull=True) return hr, ec, alert_details def update_alert_command(client: MsGraphClient, args): alert_id = args.get("alert_id") status: str = args.get("status", "") if status == "newAlert": args["status"] = "new" status = "new" params = create_data_to_update(args) client.update_alert(alert_id, params) context = {"ID": alert_id} if status: context["Status"] = status ec = {"MsGraph.Alert(val.ID && val.ID === obj.ID)": context} human_readable = f"Alert {alert_id} has been successfully updated." return human_readable, ec, context def get_users_command(client: MsGraphClient, args): users = client.get_users()["value"] outputs = [] for user in users: outputs.append({"Name": user["displayName"], "Title": user["jobTitle"], "Email": user["mail"], "ID": user["id"]}) ec = {"MsGraph.User(val.ID && val.ID === obj.ID)": outputs} table_headers = ["Name", "Title", "Email", "ID"] human_readable = tableToMarkdown("Microsoft Graph Users", outputs, table_headers, removeNull=True) return human_readable, ec, users def get_user_command(client: MsGraphClient, args): user_id = args.get("user_id") raw_user = client.get_user(user_id) user = {"Name": raw_user["displayName"], "Title": raw_user["jobTitle"], "Email": raw_user["mail"], "ID": raw_user["id"]} ec = {"MsGraph.User(val.ID && val.ID === obj.ID)": user} table_headers = ["Name", "Title", "Email", "ID"] human_readable = tableToMarkdown("Microsoft Graph User " + user_id, user, table_headers, removeNull=True) return human_readable, ec, raw_user def create_alert_comment_command(client: MsGraphClient, args): """ Adds a comment to an alert with the given id Args: client (MsGraphClient): MsGraphClient client object. args (Dict): The command's arguments dictionary. Returns: str, Dict, Dict: the human readable, parsed outputs and request's response. """ alert_id = args.get("alert_id", "") comment = args.get("comment", "") params = {"comment": comment} res = client.create_alert_comment(alert_id, params) comments = [capitalize_dict_keys_first_letter(comment) for comment in res.get("value", [])] context = {"ID": alert_id, "Comments": comments} ec = {"MsGraph.AlertComment(val.ID && val.ID == obj.ID)": context} header = f"Microsoft Security Graph Create Alert Comment - {alert_id}\n" human_readable = tableToMarkdown(header, comments, removeNull=True) return human_readable, ec, res def create_ediscovery_case_command(client: MsGraphClient, args: dict): """ """ res = client.create_edsicovery_case(args.get("display_name"), args.get("description"), args.get("external_id")) return ediscovery_cases_command_results([res], res) def close_ediscovery_case_command(client: MsGraphClient, args): """ """ client.close_edsicovery_case(args.get("case_id")) return CommandResults(readable_output=f'Case with id {args.get("case_id")} was closed successfully.') def reopen_ediscovery_case_command(client: MsGraphClient, args): """ """ client.reopen_edsicovery_case(args.get("case_id")) return CommandResults(readable_output=f'Case with id {args.get("case_id")} was reopened successfully.') def update_ediscovery_case_command(client: MsGraphClient, args): """ """ client.update_edsicovery_case(args.get("case_id"), args.get("display_name"), args.get("description"), args.get("external_id")) return CommandResults(readable_output=f'Case with id {args.get("case_id")} was updated successfully.') def release_ediscovery_custodian_command(client: MsGraphClient, args): """ """ client.release_edsicovery_custodian(args.get("case_id"), args.get("custodian_id")) return CommandResults( readable_output=f'Custodian with id {args.get("custodian_id")} was released from ' f'case with id {args.get("case_id")} successfully.' ) def activate_ediscovery_custodian_command(client: MsGraphClient, args): """ """ client.activate_edsicovery_custodian(args.get("case_id"), args.get("custodian_id")) return CommandResults( readable_output=f'Custodian with id {args.get("custodian_id")} Case was reactivated on ' f'case with id {args.get("case_id")} successfully.' ) def create_ediscovery_custodian_user_source_command(client: MsGraphClient, args): """ """ resp = client.create_edsicovery_custodian_user_source( args.get("case_id"), args.get("custodian_id"), args.get("email"), args.get("included_sources") ) return ediscovery_source_command_results(resp, DataSourceType["USER"]) def create_ediscovery_custodian_site_source_command(client: MsGraphClient, args): resp = client.create_edsicovery_custodian_site_source(args.get("case_id"), args.get("custodian_id"), args.get("site")) return ediscovery_source_command_results(resp, DataSourceType["SITE"]) def create_ediscovery_non_custodial_data_source_command(client: MsGraphClient, args): site = args.get("site") email = args.get("email") if not (bool(site) ^ bool(email)): raise ValueError("One of either the site argument or the email argument must be provided, not both") resp = client.create_ediscovery_non_custodial_data_source(args.get("case_id"), site, email) return to_msg_command_results( raw_object_list=resp, outputs_prefix="MsGraph.NoncustodialDataSource", output_key_field="DataSourceId", raw_keys_to_replace={"status": "DataSourceStatus", "id": "DataSourceId"}, ) def delete_ediscovery_case_command(client: MsGraphClient, args): client.delete_edsicovery_case(args.get("case_id")) return CommandResults(readable_output="Case was deleted successfully.") def create_ediscovery_custodian_command(client: MsGraphClient, args): res = client.create_edsicovery_custodian(args.get("case_id"), args.get("email")) return ediscovery_custodian_command_results(res) def list_ediscovery_case_command(client: MsGraphClient, args): raw_res = client.list_ediscovery_cases(args.get("case_id")) if case_list := raw_res.get("value"): demisto.info(f'returned {raw_res.get("@odata.count")} results from the api') else: case_list = [raw_res] # api doesnt return a list if only 1 result if not argToBoolean(args.get("all_results", "false")): case_list = case_list[: arg_to_number(args.get("limit", 50))] return ediscovery_cases_command_results(case_list, raw_res) def list_ediscovery_custodian_command(client: MsGraphClient, args): raw_res = client.list_ediscovery_custodians(args.get("case_id"), args.get("custodian_id")) if custodian_list := raw_res.get("value"): demisto.info(f'returned {raw_res.get("@odata.count")} results from the api') else: custodian_list = [raw_res] # api doesnt return a list if only 1 result if not argToBoolean(args.get("all_results", "false")): custodian_list = custodian_list[: arg_to_number(args.get("limit", 50))] return ediscovery_custodian_command_results(custodian_list, raw_res) def list_ediscovery_custodian_user_sources_command(client: MsGraphClient, args): return list_ediscovery_custodian_sources(client, args, DataSourceType["USER"]) def list_ediscovery_custodian_site_sources_command(client: MsGraphClient, args): return list_ediscovery_custodian_sources(client, args, DataSourceType["SITE"]) def list_ediscovery_non_custodial_data_source_command(client: MsGraphClient, args): raw_res = client.list_ediscovery_noncustodial_datasources(args.get("case_id"), args.get("data_source_id")) if source_list := raw_res.get("value"): demisto.info(f"returned {len(source_list)} results from the api") else: source_list = [raw_res] # api doesnt return a list if only 1 result if not argToBoolean(args.get("all_results", "false")): source_list = source_list[: arg_to_number(args.get("limit"))] return ediscovery_source_command_results(source_list, DataSourceType["NON_CUSTODIAL"], raw_res) def update_hold_ediscovery_custodian_command(client: MsGraphClient, args, hold_action: HoldAction): demisto.debug(f"{hold_action.value=}") res = client.update_hold_ediscovery_custodian(args.get("case_id"), args.get("custodian_id"), hold_action) status = get_status_of_operation(client, res) return CommandResults(readable_output=f"{hold_action.value.capitalize()} hold status is {status}.") def apply_hold_ediscovery_custodian_command(client: MsGraphClient, args): return update_hold_ediscovery_custodian_command(client, args, HoldAction.APPLY) def remove_hold_ediscovery_custodian_command(client: MsGraphClient, args): return update_hold_ediscovery_custodian_command(client, args, HoldAction.REMOVE) def update_ediscovery_search_command(client: MsGraphClient, args): client.update_ediscovery_search( args.get("case_id"), args.get("search_id"), args.get("display_name"), args.get("description"), args.get("content_query"), args.get("data_source_scopes"), ) return CommandResults(readable_output=f'eDiscovery search {args.get("search_id")} was updated successfully.') def delete_ediscovery_search_command(client: MsGraphClient, args): client.delete_ediscovery_search(args.get("case_id"), args.get("search_id")) return CommandResults(readable_output=f'eDiscovery search {args.get("search_id")} was deleted successfully.') def get_operation_id_from_location_header(location_url: str | None) -> str | None: """ Extract the operation ID from a Microsoft Graph Location header URL. The Location header can appear in one of two formats: - .../ediscoveryCases('<caseId>')/operations('<operationId>') - .../ediscoveryCases/<caseId>/operations/<operationId> Args: location_url: The value of the Location header returned by the API. Returns: The extracted operation ID, or None if it could not be parsed. """ if not location_url: return None operation_id_match = re.search(r"operations\('([^']+)'\)", location_url) or re.search(r"operations/([^/?]+)", location_url) return operation_id_match.group(1) if operation_id_match else None def purge_ediscovery_data_command(client: MsGraphClient, args): resp = client.purge_ediscovery_data( args.get("case_id"), args.get("search_id"), args.get("purge_type"), args.get("purge_areas") ) status = get_status_of_operation(client, resp) operation_id = get_operation_id_from_location_header(resp.headers.get("Location")) readable_output = f"eDiscovery purge status is {status}.\n- Operation ID: {operation_id}" outputs = {"OperationID": operation_id, "Status": status} remove_nulls_from_dictionary(outputs) return CommandResults( readable_output=readable_output, outputs=outputs, outputs_prefix="MsGraph.eDiscoveryCase.Purge", outputs_key_field="OperationID", ) def run_estimate_statistics_command(client: MsGraphClient, args) -> CommandResults: case_id = args.get("case_id") search_id = args.get("search_id") statistics_options = argToList(args.get("statistics_options", [])) # Start the estimate statistics operation client.start_estimate_statistics_request(case_id, search_id, statistics_options) demisto.info(f"[run_estimate_statistics_command] Estimate statistics started for case {case_id}, search {search_id}.") # Return confirmation only return CommandResults( readable_output=f"Estimate statistics request initiated for case `{case_id}`, search `{search_id}`.", ) def create_ediscovery_case_hold_policy_command( client: MsGraphClient, args, ) -> CommandResults: """ Create a legal hold policy for an eDiscovery case. Args: client: Microsoft Graph client. args: Command arguments. Returns: CommandResults containing the created hold policy. """ raw_resp = client.create_ediscovery_case_hold_policy( args.get("case_id"), args.get("display_name"), args.get("description"), args.get("content_query"), ) human_readable = tableToMarkdown( name="Created eDiscovery Hold Policy", t={ "Display Name": raw_resp.get("displayName"), "Id": raw_resp.get("id"), "Status": raw_resp.get("status"), }, ) return CommandResults( outputs_prefix="MsGraph.eDiscoveryCase.HoldPolicy", outputs_key_field="ID", outputs=capitalize_dict_keys_first_letter(raw_resp), readable_output=human_readable, raw_response=raw_resp, ) def delete_ediscovery_case_hold_policy_command( client: MsGraphClient, args: Any, ) -> CommandResults: """ Delete a legal hold policy from an eDiscovery case. Args: client: Microsoft Graph client. args: Command arguments. Returns: CommandResults with a success message. """ hold_policy_id = args.get("hold_policy_id") case_id = args.get("case_id") client.delete_ediscovery_case_hold_policy( case_id, hold_policy_id, ) return CommandResults( readable_output=(f"The deletion request for hold policy {hold_policy_id} in case {case_id} was sent successfully."), ) def update_ediscovery_case_policy_command( client: MsGraphClient, args, ) -> CommandResults: """ Update a legal hold policy for an eDiscovery case. Args: client: Microsoft Graph client. args: Command arguments. Returns: CommandResults with a success message. """ case_id = args.get("case_id") hold_policy_id = args.get("hold_policy_id") description = args.get("description") content_query = args.get("content_query") if not description and not content_query: raise DemistoException("Please provide at least one field to update: description and/or content_query.") try: client.update_ediscovery_case_policy( case_id, hold_policy_id, description, content_query, ) except DemistoException as e: err = str(e) # Only enrich message when the user tried to update contentQuery and we recognize the failure if content_query and "ErrorRuleNotFoundException" in err: raise DemistoException( f"Failed to update hold policy '{hold_policy_id}' content query.\n\n" "This can happen when the hold policy was created using the legacy Security & Compliance (PowerShell/RPS) flow " "and the underlying hold rule is not available to be updated via Microsoft Graph yet.\n\n" "Recommended actions:\n" "1) Retry the hold policy in Purview (Policy actions → Retry) and try again.\n" "2) If the issue persists, recreate the hold policy using Microsoft Graph Security and " "then manage it via the Graph commands.\n\n" f"Error message: {err}" ) from e raise e return CommandResults(readable_output=f'Hold policy {args.get("hold_policy_id")} was updated successfully.') def list_ediscovery_case_hold_policy_command( client: MsGraphClient, args, ) -> CommandResults: """ List or retrieve legal hold policies for an eDiscovery case. Args: client: Microsoft Graph client. args: Command arguments. Returns: CommandResults containing hold policy data. """ case_id = args.get("case_id") hold_policy_id = args.get("hold_policy_id") limit = None if argToBoolean(args.get("all_results")) else int(args.get("limit", 50)) if hold_policy_id: raw_res = client.get_ediscovery_case_hold_policy(case_id, hold_policy_id) hold_list = [raw_res] else: raw_res = client.list_ediscovery_case_hold_policy(case_id, limit) hold_list = raw_res.get("value", []) demisto.debug(f"returned {len(hold_list)} results from the api") hr = [ { "Display Name": hold.get("displayName"), "Id": hold.get("id"), "Status": hold.get("status"), } for hold in hold_list ] return CommandResults( outputs_prefix="MsGraph.eDiscoveryCase.HoldPolicy", outputs_key_field="ID", outputs=[capitalize_dict_keys_first_letter(hold) for hold in hold_list], readable_output=tableToMarkdown(name="eDiscovery Case Hold Policies", t=hr), raw_response=raw_res, ) def list_case_operation_command( client: MsGraphClient, args, ) -> CommandResults | list[dict | CommandResults]: """ List or retrieve operations for an eDiscovery case. Optionally downloads the export file when operation_id is provided and ediscovery-export-file=true. """ case_id = args.get("case_id") operation_id = args.get("operation_id") download_file = argToBoolean(args.get("download_file", "false")) all_results = argToBoolean(args.get("all_results", "false")) limit = None if all_results else int(args.get("limit", 50)) file_result = None if operation_id: raw_res = client.get_case_operation(case_id, operation_id) operation_list = [raw_res] if download_file and operation_list: file_result = _download_operation_export_file(client, operation_list[0]) else: raw_res = client.list_case_operation(case_id, limit) operation_list = raw_res.get("value") or [] if isinstance(operation_list, dict): operation_list = [operation_list] demisto.debug(f"returned {len(operation_list)} results from the api") hr = [ { "ID": op.get("id"), "Action": op.get("action"), "Status": op.get("status"), "Created By": op.get("createdBy"), "Link to download a file": _extract_export_download_url(op), } for op in operation_list ] command_result = CommandResults( outputs_prefix="MsGraph.eDiscoveryCase.Operation", outputs_key_field="ID", outputs=[capitalize_dict_keys_first_letter(op) for op in operation_list], readable_output=tableToMarkdown( name="eDiscovery Case Operations", t=hr, headers=["ID", "Action", "Status", "Created By", "Link to download a file"], removeNull=True, ), raw_response=raw_res, ) return [file_result, command_result] if file_result else command_result def _extract_export_download_url(operation: dict) -> str | None: """ exportFileMetadata can be a dict or a list of dicts (sometimes). Return downloadUrl if present. """ meta = operation.get("exportFileMetadata") if isinstance(meta, dict): return meta.get("downloadUrl") if isinstance(meta, list) and meta and isinstance(meta[0], dict): return meta[0].get("downloadUrl") return None def _extract_filename_from_headers( headers: Optional[dict[str, str]] = None, default: str = "ediscovery_export.zip", ) -> str: """ Extract a filename from the Content-Disposition header (expects `filename=...`). Args: headers: Response headers mapping. default: Filename to return if Content-Disposition is missing or unparseable. Returns: The extracted filename, or `default`. """ headers = headers or {} cd = headers.get("Content-Disposition") or headers.get("content-disposition") or "" m = re.search(r'(?i)\bfilename\s*=\s*"?([^";]+)"?', cd) return (m.group(1).strip() if m else "") or default def _download_operation_export_file(client: MsGraphClient, operation: dict) -> dict | None: """ Download the export file referenced by an operation and return a fileResult. Returns None if the operation has no valid download URL. """ download_url = _extract_export_download_url(operation) if not isinstance(download_url, str) or not download_url: return None res = client.download_export_file(download_url) status = getattr(res, "status_code", None) ok = bool(getattr(res, "ok", False)) if not ok: text = (getattr(res, "text", "") or "")[:500] raise DemistoException(f"Failed to download export file. HTTP {status}. {text}") file_bytes = getattr(res, "content", None) or b"" if len(file_bytes) == 0: raise DemistoException(f"Downloaded export file is empty. HTTP {status}.") filename = _extract_filename_from_headers(getattr(res, "headers", None), default="ediscovery_export.zip") return fileResult(filename=filename, data=file_bytes) def export_result_ediscovery_data_command( client: MsGraphClient, args: Any, ) -> CommandResults: """ Export search results from an eDiscovery case. Args: client: Microsoft Graph client. args: Command arguments. Returns: CommandResults containing the export operation location. """ resp = client.export_result_ediscovery_data( args.get("case_id"), args.get("search_id"), args.get("additional_options"), args.get("export_criteria"), args.get("export_format"), args.get("cloud_attachment_version"), args.get("description"), args.get("display_name"), args.get("document_version"), args.get("export_location"), ) operation_url = resp.headers.get("Location") if not operation_url: raise DemistoException("Missing Location header in exportResult response") case_id_from_url = re.search(r"ediscoveryCases\('([^']+)'\)", operation_url) or re.search( r"ediscoveryCases/([^/]+)/", operation_url ) case_id = (case_id_from_url.group(1) if case_id_from_url else args.get("case_id")) or "N/A" operation_id = get_operation_id_from_location_header(operation_url) or None readable_output = ( "eDiscovery export request was submitted successfully.\n" f"- Case ID: {case_id}\n" f"- Operation ID: {operation_id}\n" ) outputs = {"Location": operation_url, "OperationID": operation_id, "CaseID": case_id} return CommandResults(readable_output=readable_output, outputs=outputs, outputs_prefix="MsGraph.eDiscoveryCase.Export") # @polling_function( # "msg-get-last-estimate-statistics-operation", # timeout=arg_to_number(demisto.args().get("timeout_in_seconds", 600)), # requires_polling_arg=False, # ) def _get_last_estimate_statistics_command(args, client: MsGraphClient) -> PollResult: case_id = args.get("case_id") search_id = args.get("search_id") resp = client.get_last_estimate_statistics_operation(case_id, search_id) status = (resp.get("status") or "").lower() if status not in ("succeeded", "completed"): demisto.debug(f"[get_last_estimate_statistics_command] Status: {status}, scheduling next poll.") return PollResult( continue_to_poll=True, args_for_next_run=args, response=None, partial_result=CommandResults( readable_output=f"Estimate statistics operation is still running... (Status: {status})" ), ) # Completed — return final statistics stats_info = { "Operation ID": resp.get("id"), "Status": resp.get("status"), "Progress": resp.get("percentProgress"), "Created": resp.get("createdDateTime"), "Last Modified": resp.get("lastActionDateTime"), "Indexed Items": resp.get("indexedItemCount"), "Indexed Size (bytes)": resp.get("indexedItemsSize"), "Unindexed Items": resp.get("unindexedItemCount"), "Unindexed Size (bytes)": resp.get("unindexedItemsSize"), "Total Items": resp.get("totalItemCount"), "Total Size (bytes)": resp.get("totalItemsSize"), "Mailbox Count": resp.get("mailboxCount"), "Site Count": resp.get("siteCount"), } readable_output = tableToMarkdown( f"eDiscovery Estimate Statistics for Search `{search_id}`", stats_info, removeNull=True, ) return PollResult( response=CommandResults( readable_output=readable_output, outputs_prefix="MsGraph.eDiscovery.EstimateStatistics", outputs_key_field="id", outputs=resp, raw_response=resp, ) ) # Decorated version for XSOAR runtime get_last_estimate_statistics_command = polling_function( "msg-get-last-estimate-statistics-operation", timeout=arg_to_number(demisto.args().get("timeout_in_seconds", 600)), requires_polling_arg=False, )(_get_last_estimate_statistics_command) def create_ediscovery_search_command(client: MsGraphClient, args): resp = client.create_ediscovery_search( args.get("case_id"), args.get("display_name"), args.get("description"), args.get("content_query"), args.get("data_source_scopes"), ) return to_ediscovery_search_command_results(resp) def list_ediscovery_search_command(client: MsGraphClient, args): raw_res = client.list_ediscovery_search(args.get("case_id"), args.get("search_id")) if case_list := raw_res.get("value"): demisto.info(f"returned {len(case_list)} results from the api") else: case_list = [raw_res] if not argToBoolean(args.get("all_results", "false")): case_list = case_list[: arg_to_number(args.get("limit"))] return to_ediscovery_search_command_results(case_list, raw_res) def test_auth_code_command(client: MsGraphClient, args): """ Called to test authorization code flow (since integration context cant be accessed during test_module) Calls list cases with no arguments """ permissions = args.get("permission_type", "all") if permissions == "all": permissions = "ediscovery, alerts, threat assessment" for permission in argToList(permissions): try: demisto.debug(f"checking permission {permission}") match permission: case "ediscovery": list_ediscovery_case_command(client, {}) case "alerts": test_function(client, args, True) case "threat assessment": list_threat_assessment_requests_command(client, {}) except Exception as e: raise DemistoException( f"Authorization was not successful for permission {permission} Check that you have the required permissions" ) from e return CommandResults(readable_output="Authentication was successful.") def advanced_hunting_command(client: MsGraphClient, args: dict) -> list[CommandResults] | CommandResults: """ Sends a query for the advanced hunting tool. Args: client(Client): Microsoft Graph Security's client to preform the API calls. args(Dict): Demisto arguments: - query (str) - The query to run (required) - limit (int) - number of entries in the result, -1 for no limit. - timeout (int) - waiting time for command execution. Returns: """ query = args["query"] # required argument limit = arg_to_number(args["limit"]) # default value is defined timeout = arg_to_number(args["timeout"]) # default value is defined query = query_set_limit(query, limit) # type:ignore[arg-type] response = client.advanced_hunting_request(query=query, timeout=timeout) # type:ignore[arg-type] results = response.get("results") schema = response.get("schema", {}) headers = [item.get("name") for item in schema] context_result = {"query": query, "results": results} human_readable_table = tableToMarkdown(name=f" Result of query: {query}:", t=results, headers=headers) microsoft_365_defender_context = demisto.params().get("microsoft_365_defender_context") command_result_ms_graph = CommandResults( outputs_prefix="MsGraph.Hunt", outputs_key_field="query", outputs=context_result, readable_output=human_readable_table ) if microsoft_365_defender_context: command_result_microsoft_defender = CommandResults( outputs_prefix="Microsoft365Defender.Hunt", outputs_key_field="query", outputs=context_result, readable_output="See Results Above", ) return [command_result_ms_graph, command_result_microsoft_defender] return command_result_ms_graph def get_list_security_incident_command(client: MsGraphClient, args: dict) -> CommandResults: """ Retrieve a list of security incidents or a single incident based on the provided arguments. Args: client (MsGraphClient): The Microsoft Graph client object. args (dict): A dictionary containing the command arguments: - 'timeout' (str): The timeout for the request in seconds. - 'incident_id' (str): The ID of the incident to retrieve. If None, retrieves a list of incidents. Returns: CommandResults: The command results object containing the outputs and readable output. """ timeout = arg_to_number(args["timeout"]) # default value is defined incident_id = arg_to_number(args.get("incident_id")) extra_data = argToBoolean(args.get("extra_data", False)) if incident_id: # Case of single incident url_suffix = f"security/incidents/{incident_id}" if extra_data: # Include the incident's related alerts as part of the response. url_suffix += "?$expand=alerts" incident_response = client.get_incidents_request(url_suffix, timeout) # type:ignore[arg-type] if incident_response.get("@odata.context"): del incident_response["@odata.context"] name = f"Incident No. {incident_id}:" readable_incident = convert_single_incident_to_readable(incident_response) headers = list(readable_incident) outputs = incident_response else: # Case of list incidents incidents_list = get_list_incidents(client, args) name = "Incidents:" readable_incident = convert_list_incidents_to_readable(incidents_list) # type:ignore[assignment] headers = list(readable_incident[0]) outputs = incidents_list # type:ignore[assignment] human_readable_table = tableToMarkdown(name=name, t=readable_incident, headers=headers) return CommandResults( outputs_prefix="MsGraph.Incident", outputs_key_field="id", outputs=outputs, readable_output=human_readable_table ) def update_incident_command(client: MsGraphClient, args: dict) -> CommandResults: """ Update an incident. Args: client(Client): Microsoft Graph Security's client to preform the API calls. args(Dict): Demisto arguments: - incident_id (int) - incident's id (required) - status (str) - Specifies the current status of the alert. Possible values are: (Active, Resolved or Redirected) - assigned_to (str) - Owner of the incident. - classification (str) - Specification of the alert. Possible values are: Unknown, FalsePositive, TruePositive. - determination (str) - Specifies the determination of the alert. Possible values are: NotAvailable, Apt, Malware, SecurityPersonnel, SecurityTesting, UnwantedSoftware, Other. - severity (str): Indicates the possible impact on assets. The higher the severity, the bigger the impact. Typically, higher severity items require the most immediate attention. The possible values are: unknown, informational, low, medium, high, unknownFutureValue. - resolving_comment (str): User input that explains the resolution of the incident and the classification choice. It contains free editable text. - custom_tags - Custom tags associated with an incident. Separated by commas without spaces (CSV) for example: tag1,tag2,tag3. Returns: CommandResults """ incident_id = arg_to_number(args["incident_id"]) # required argument status = args.get("status") assigned_to = args.get("assigned_to") determination = args.get("determination") classification = args.get("classification") severity = args.get("severity") resolving_comment = args.get("resolving_comment") custom_tags = argToList(args.get("custom_tags")) timeout = arg_to_number(args["timeout"]) # default value is defined updated_incident = client.update_incident_request( incident_id=incident_id, # type:ignore[arg-type] status=status, # type:ignore[arg-type] assigned_to=assigned_to, classification=classification, determination=determination, severity=severity, resolving_comment=resolving_comment, custom_tags=custom_tags, timeout=timeout, # type:ignore[arg-type] ) if updated_incident.get("@odata.context"): del updated_incident["@odata.context"] readable_incident = convert_single_incident_to_readable(updated_incident) human_readable_table = tableToMarkdown( name=f"Updated incident No. {incident_id}:", t=readable_incident, headers=list(readable_incident) ) return CommandResults( outputs_prefix="MsGraph.Incident", outputs_key_field="id", outputs=updated_incident, readable_output=human_readable_table ) def test_function(client: MsGraphClient, args, has_access_to_context=False): # pragma: no cover """ Args: has_access_to_context (bool): Whether this function is called from a command that allows this integration to access the context. When called from the test button on an integration, we dont have access to the integration context. Since auth code workflow depends on reading from and writing to the context, if we dont have access, this function cannot run successfully, so we will throw an exception. Performs basic GET request to check if the API is reachable and authentication is successful. Returns: 'ok' if connection is successful. Raises: DemistoException: If using auth code flow and called from test_module """ if ( not has_access_to_context and hasattr(client.ms_client, "grant_type") and client.ms_client.grant_type == AUTHORIZATION_CODE ): raise DemistoException( "Test module is not available for the authorization code flow. Use the msg-auth-test command instead." ) response = client.ms_client.http_request(method="GET", url_suffix=CMD_URL, params={"$top": 1}, resp_type="response") try: data = response.json() if response.text else {} if not response.ok: return_error( f'API call to MS Graph Security failed. Please check authentication related parameters.' f' [{response.status_code}] - {demisto.get(data, "error.message")}' ) params: dict = demisto.params() if params.get("isFetch"): fetch_limit = arg_to_number(params.get("fetch_limit")) or MAX_ITEMS_PER_RESPONSE if fetch_limit > MAX_ITEMS_PER_RESPONSE: raise DemistoException( f"The fetch limit per type cannot be higher than {MAX_ITEMS_PER_RESPONSE}, " "due to a Microsoft limitation when fetching incidents." ) fetch_time = params.get("fetch_time", "3 days") fetch_incidents_type = argToList(params.get("fetch_incidents_type")) time_from = parse_date_range(fetch_time, date_format=TIMESTAMP_FORMAT)[0] time_to = datetime.now().strftime(TIMESTAMP_FORMAT) if "Alerts" in fetch_incidents_type: fetch_filter = params.get("fetch_filter", "") fetch_service_sources = params.get("fetch_service_sources", "") filter_query = create_filter_query(fetch_filter, fetch_service_sources) args = {"time_to": time_to, "time_from": time_from, "filter": filter_query} alerts_params = create_search_alerts_filters(args, is_fetch=True) try: client.search_alerts(alerts_params)["value"] except Exception as e: if "Invalid ODATA query filter" in e.args[0]: raise DemistoException( "Wrong alerts filter format, correct usage: {property} eq '{property-value}'\n\n" + e.args[0] ) raise e if "Incidents" in fetch_incidents_type: fetch_incidents_filter = params.get("fetch_incidents_filter", "") filter_expression = f"createdDateTime gt {time_from} and createdDateTime le {time_to}" if fetch_incidents_filter: # Wrap in parentheses so an `or` clause can't escape the time window (matches the real fetch query). filter_expression += f" and ({fetch_incidents_filter})" url_suffix = f"security/incidents?$top=1&$filter={filter_expression}" try: client.get_incidents_request(url_suffix, FETCH_INCIDENTS_TIMEOUT) except Exception as e: if "Invalid ODATA query filter" in e.args[0]: raise DemistoException( "Wrong incidents filter format, correct usage: {property} eq '{property-value}'\n\n" + e.args[0] ) raise e return "ok", None, None except TypeError as ex: demisto.debug(str(ex)) return_error( f"API call to MS Graph Security failed, could not parse result. " f"Please check authentication related parameters. [{response.status_code}]" ) def get_message_user(client, message_user): is_email = re.search(EMAIL_REGEX, message_user) if is_email: user_result = (client.get_user_id(message_user)).get("value") if not user_result: raise DemistoException(f"{message_user} is not a valid user") return user_result[0].get("id") return message_user def is_base_64(string: str) -> bool: # pragma: no cover """ Validate if string is base 64 encoded. Args: string (str): String to validate. Returns: bool: True if the string is base 64 encoded , else False. """ try: if isinstance(string, str): # If there's any unicode here, an exception will be thrown and the function will return false string_bytes = bytes(string, "ascii") elif isinstance(string, bytes): string_bytes = string else: raise ValueError("Argument must be string or bytes") return base64.b64encode(base64.b64decode(string_bytes)) == string_bytes except Exception: return False def get_content_data(entry_id, content_data): # pragma: no cover if not (entry_id or content_data) or (entry_id and content_data): raise DemistoException("Just one of entry_id or content_data arguments has to be provided.") try: if entry_id: file = demisto.getFilePath(entry_id) file_path = file["path"] with open(file_path, "rb") as fp: content = base64.b64encode(fp.read()) return str(content, encoding="utf-8") if content_data: return content_data if is_base_64(content_data) else base64.b64encode(content_data) except Exception as e: raise DemistoException(f"Failed loading content data: {e}") def get_result_outputs(result) -> Dict: output = { "ID": result.get("id"), "Created DateTime": result.get("createdDateTime"), "Content Type": result.get("contentType"), "Expected Assessment": result.get("expectedAssessment"), "Category": result.get("category"), "Status": result.get("status"), "Request Source": result.get("requestSource"), "Recipient Email": result.get("recipientEmail"), "Destination Routing Reason": result.get("destinationRoutingReason"), "URL": result.get("url"), "File Name": result.get("fileName"), } if created_by := result.get("createdBy"): output["Created User ID"] = created_by.get("user", {}).get("id") output["Created Username"] = created_by.get("user", {}).get("displayName") if results := result.get("results"): output["Result Type"] = results[0].get("resultType") output["Result message"] = results[0].get("message") return output def get_threat_assessment_request(client: MsGraphClient, request_id): result = client.get_threat_assessment_request(request_id) outputs = get_result_outputs(result) readable_output = tableToMarkdown("Threat assessment request:", outputs, removeNull=True) return [ CommandResults( readable_output=readable_output, raw_response=result, outputs=outputs, outputs_prefix="MSGraphMail.AssessmentRequest" ) ] @polling_function( "msg-create-mail-assessment-request", timeout=arg_to_number(demisto.args().get("timeout_in_seconds", 720)), requires_polling_arg=False, ) def create_mail_assessment_request_command(args, client: MsGraphClient) -> PollResult | CommandResults: if not (request_id := args.get("request_id")): message_user = get_message_user(client, args.get("message_user")) result = client.create_mail_assessment_request( args.get("recipient_email"), args.get("expected_assessment"), args.get("category"), message_user, args.get("message_id"), ) request_id = result.get("id") result = client.get_threat_assessment_request(request_id) status = result.get("status") demisto.debug(f"status is: {status}") if status == "completed" or result.get("results"): outputs = get_result_outputs(result) outputs["Message ID"] = args.get("message_id") readable_output = tableToMarkdown("Mail assessment request:", outputs, removeNull=True) results = CommandResults( readable_output=readable_output, raw_response=result, outputs=outputs, outputs_prefix="MSGraphMail.MailAssessment" ) return PollResult(response=results) else: return PollResult( continue_to_poll=True, args_for_next_run={"request_id": request_id, **args}, response=None, partial_result=CommandResults(readable_output="The status is pending, still waiting to get results..."), ) @polling_function( "msg-create-email-file-assessment-request", timeout=arg_to_number(demisto.args().get("timeout_in_seconds", 720)), requires_polling_arg=False, ) def create_email_file_request_command(args, client: MsGraphClient) -> PollResult | CommandResults: if not (request_id := args.get("request_id")): content_data = get_content_data(args.get("entry_id"), args.get("content_data")) result = client.create_email_file_assessment_request( args.get("recipient_email"), args.get("expected_assessment"), args.get("category"), content_data ) request_id = result.get("id") demisto.debug(f"got request id: {request_id}") result = client.get_threat_assessment_request(request_id) status = result.get("status") demisto.debug(f"status is: {status}") if status == "completed" or result.get("results"): outputs = get_result_outputs(result) readable_output = tableToMarkdown("Email file assessment request results:", outputs, removeNull=True) results = CommandResults( readable_output=readable_output, raw_response=result, outputs=outputs, outputs_prefix="MSGraphMail.EmailAssessment" ) return PollResult(response=results) else: return PollResult( continue_to_poll=True, args_for_next_run={"request_id": request_id, **args}, response=None, partial_result=CommandResults(readable_output="The status is pending, still waiting to get results..."), ) @polling_function("msg-create-file-assessment-request", requires_polling_arg=False) def create_file_assessment_request_command(args, client) -> PollResult | CommandResults: if not (request_id := args.get("request_id")): content_data = get_content_data(args.get("entry_id"), args.get("content_data")) demisto.debug(f"got content data: {content_data}") result = client.create_file_assessment_request( args.get("expected_assessment"), args.get("category"), args.get("file_name"), content_data ) request_id = result.get("id") demisto.debug(f"got request id: {request_id}") result = client.get_threat_assessment_request_status(request_id) status = result.get("status") demisto.debug(f"status is: {status}") if status == "completed": result = client.get_threat_assessment_request(request_id) outputs = get_result_outputs(result) readable_output = tableToMarkdown("File assessment request results:", outputs, removeNull=True) results = CommandResults( readable_output=readable_output, raw_response=result, outputs=outputs, outputs_prefix="MSGraphMail.FileAssessment" ) return PollResult(response=results) else: return PollResult( continue_to_poll=True, args_for_next_run={"request_id": request_id, **args}, response=None, partial_result=CommandResults(readable_output="The status is pending, still waiting to get results..."), ) @polling_function( "msg-create-url-assessment-request", timeout=arg_to_number(demisto.args().get("timeout_in_seconds", 720)), requires_polling_arg=False, ) def create_url_assessment_request_command(args, client: MsGraphClient) -> PollResult | CommandResults: if not (request_id := args.get("request_id")): result = client.create_url_assessment_request(args.get("expected_assessment"), args.get("category"), args.get("url")) request_id = result.get("id") result = client.get_threat_assessment_request_status(request_id) status = result.get("status") demisto.debug(f"status is : {status}") if status == "completed": result = client.get_threat_assessment_request(request_id) outputs = get_result_outputs(result) readable_output = tableToMarkdown("URL assessment request results:", outputs, removeNull=True) results = CommandResults( readable_output=readable_output, raw_response=result, outputs=outputs, outputs_prefix="MSGraphMail.UrlAssessment" ) return PollResult(response=results) else: return PollResult( continue_to_poll=True, args_for_next_run={"request_id": request_id, **args}, response=None, partial_result=CommandResults(readable_output="The status is pending, still waiting to get results..."), ) def list_threat_assessment_requests_command(client: MsGraphClient, args) -> list[CommandResults]: command_results = [] limit = args.get("limit") if request_id := args.get("request_id"): return get_threat_assessment_request(client, request_id) result = client.list_threat_assessment_requests( args.get("filter"), args.get("order_by"), args.get("sort_order"), args.get("next_token") ) outputs = [] requests_list = result.get("value") if limit: requests_list = requests_list[:limit] for req in requests_list: outputs.append(get_result_outputs(req)) readable_outputs = tableToMarkdown("Threat assessment request results:", outputs, removeNull=True) command_results.append( CommandResults( readable_output=readable_outputs, raw_response=result, outputs=outputs, outputs_prefix="MSGraphMail.AssessmentRequest" ) ) skip_token_field = result.get("@odata.nextLink") demisto.debug(f"skip_token_field: {skip_token_field}") if skip_token_field: next_token_value: List[str] = re.split(r"skip[t|T]oken=", skip_token_field) if len(next_token_value) > 1: next_token: str = next_token_value[1] command_results.append( CommandResults( readable_output=f"Next token is: {next_token}\n" if next_token else None, outputs={"next_token": next_token}, outputs_prefix="MsGraph.AssessmentRequestNextToken", ) ) return command_results def main(): params: dict = demisto.params() args: dict = demisto.args() tenant = params.get("creds_tenant_id", {}).get("password") or params.get("tenant_id") auth_and_token_url = params.get("creds_auth_id", {}).get("password") or params.get("auth_id", "") enc_key = params.get("creds_enc_key", {}).get("password") or params.get("enc_key") use_ssl = not params.get("insecure", False) proxy = params.get("proxy", False) certificate_thumbprint = params.get("creds_certificate", {}).get("identifier") or params.get("certificate_thumbprint") private_key = replace_spaces_in_credential(params.get("creds_certificate", {}).get("password")) or params.get("private_key") managed_identities_client_id = get_azure_managed_identities_client_id(params) self_deployed: bool = params.get("self_deployed", False) or managed_identities_client_id is not None azure_cloud = get_azure_cloud(params, "MicrosoftGraphSecurity") if not managed_identities_client_id: if not self_deployed and not enc_key: raise DemistoException( "Key must be provided. For further information see " "https://xsoar.pan.dev/docs/reference/articles/microsoft-integrations---authentication" ) elif not enc_key and not (certificate_thumbprint and private_key): raise DemistoException("Key or Certificate Thumbprint and Private Key must be provided.") commands = { "test-module": test_function, "msg-auth-test": test_auth_code_command, "msg-search-alerts": search_alerts_command, "msg-get-alert-details": get_alert_details_command, "msg-update-alert": update_alert_command, "msg-get-users": get_users_command, "msg-get-user": get_user_command, "msg-create-alert-comment": create_alert_comment_command, # eDiscovery commands "msg-create-ediscovery-case": create_ediscovery_case_command, "msg-list-ediscovery-cases": list_ediscovery_case_command, "msg-update-ediscovery-case": update_ediscovery_case_command, "msg-close-ediscovery-case": close_ediscovery_case_command, "msg-reopen-ediscovery-case": reopen_ediscovery_case_command, "msg-delete-ediscovery-case": delete_ediscovery_case_command, "msg-create-ediscovery-custodian": create_ediscovery_custodian_command, "msg-list-ediscovery-custodians": list_ediscovery_custodian_command, "msg-release-ediscovery-custodian": release_ediscovery_custodian_command, "msg-activate-ediscovery-custodian": activate_ediscovery_custodian_command, "msg-create-ediscovery-custodian-user-source": create_ediscovery_custodian_user_source_command, "msg-list-ediscovery-custodian-user-sources": list_ediscovery_custodian_user_sources_command, "msg-create-ediscovery-custodian-site-source": create_ediscovery_custodian_site_source_command, "msg-list-ediscovery-custodian-site-sources": list_ediscovery_custodian_site_sources_command, "msg-create-ediscovery-non-custodial-data-source": create_ediscovery_non_custodial_data_source_command, "msg-list-ediscovery-non-custodial-data-sources": list_ediscovery_non_custodial_data_source_command, "msg-apply-hold-ediscovery-custodian": apply_hold_ediscovery_custodian_command, "msg-remove-hold-ediscovery-custodian": remove_hold_ediscovery_custodian_command, "msg-create-ediscovery-search": create_ediscovery_search_command, "msg-update-ediscovery-search": update_ediscovery_search_command, "msg-list-ediscovery-searchs": list_ediscovery_search_command, "msg-delete-ediscovery-search": delete_ediscovery_search_command, "msg-purge-ediscovery-data": purge_ediscovery_data_command, "msg-run-estimate-statistics": run_estimate_statistics_command, "msg-advanced-hunting": advanced_hunting_command, "msg-list-security-incident": get_list_security_incident_command, "msg-update-security-incident": update_incident_command, "msg-create-ediscovery-case-hold-policy": create_ediscovery_case_hold_policy_command, "msg-delete-ediscovery-case-hold-policy": delete_ediscovery_case_hold_policy_command, "msg-update-ediscovery-case-hold-policy": update_ediscovery_case_policy_command, "msg-list-ediscovery-case-hold-policy": list_ediscovery_case_hold_policy_command, "msg-list-case-operation": list_case_operation_command, "msg-export-result-ediscovery-data": export_result_ediscovery_data_command, } command = demisto.command() LOG(f"Command being called is {command}") try: auth_code = params.get("auth_code", {}).get("password") redirect_uri = params.get("redirect_uri") grant_type = AUTHORIZATION_CODE if auth_code and redirect_uri else CLIENT_CREDENTIALS client: MsGraphClient = MsGraphClient( tenant_id=tenant, auth_code=auth_code, auth_id=auth_and_token_url, enc_key=enc_key, redirect_uri=redirect_uri, app_name=APP_NAME, azure_cloud=azure_cloud, azure_ad_endpoint=azure_cloud.endpoints.active_directory, token_retrieval_url=urljoin(azure_cloud.endpoints.active_directory, f"/{tenant}/oauth2/v2.0/token"), base_url=urljoin(azure_cloud.endpoints.microsoft_graph_resource_id, "/v1.0/"), verify=use_ssl, proxy=proxy, self_deployed=self_deployed, certificate_thumbprint=certificate_thumbprint, private_key=private_key, managed_identities_client_id=managed_identities_client_id, grant_type=grant_type, ) if command == "fetch-incidents": incidents_and_alerts = fetch_incidents_and_alerts(client, params) demisto.incidents(incidents_and_alerts) elif command == "msg-create-mail-assessment-request": return_results(create_mail_assessment_request_command(args, client)) elif command == "msg-create-email-file-assessment-request": return_results(create_email_file_request_command(args, client)) elif command == "msg-create-file-assessment-request": return_results(create_file_assessment_request_command(args, client)) elif command == "msg-create-url-assessment-request": return_results(create_url_assessment_request_command(args, client)) elif command == "msg-list-threat-assessment-requests": return_results(list_threat_assessment_requests_command(client, args)) elif command == "msg-get-last-estimate-statistics-operation": return_results(get_last_estimate_statistics_command(args, client)) elif command == "ms-graph-security-auth-reset": return_results(reset_auth()) elif demisto.command() == "msg-generate-login-url": return_results(generate_login_url(client.ms_client)) else: if command not in commands: raise NotImplementedError(f"The provided command {command} was not implemented.") command_res = commands[command](client, args) # type: ignore if isinstance(command_res, (CommandResults | list)): return_results(command_res) else: human_readable, entry_context, raw_response = command_res # pylint: disable=E0633 # type: ignore return_outputs(readable_output=human_readable, outputs=entry_context, raw_response=raw_response) except Exception as err: return_error(str(err)) if __name__ in ["__main__", "builtin", "builtins"]: main()