Introduction
In my last blog post I compared using Microsoft Graph with the Microsoft Sentinel REST APIs. If you have your Microsoft Sentinel hooked up with Microsoft Defender, I recommend using the Microsoft Graph as much as possible.
It appears that Microsoft will be pushing more and more to the Graph, so it makes sense to use it. It is very easy to use, with easy-to-understand URLs that utilize common parameters.
It can be used with many different programming languages, and the Microsoft Graph Explorer shows code for C#, CLI, Go, Java, JavaScript, PHP, Python, and the one we will be looking at, PowerShell.
Using PowerShell
Much like the Microsoft Sentinel REST APIs, you can either call the Microsoft Graph REST APIs directly or use some pre-built PowerShell class libraries. For this blog post, we will use the class libraries.
It should be noted that, unlike Microsoft Sentinel, there are multiple PowerShell class libraries for the Microsoft Graph. We will be focusing on the “Microsoft.Graph.Security” one for this post.
If you do not already have this loaded, you can use the “Install-Module” PowerShell command to load it. I won’t go into all the details on this command, but you can go to Install the Microsoft Graph PowerShell SDK | Microsoft Learn to get more information on how to do the install.
You can run:
Get-Help Microsoft.Graph.Security
To get a list of all the command available. If you run that command, you will see “Get-MgSecurityAlert” as well as “Get-MgSecurityIncident”. These match the calls to “https://graph.microsoft.com/v1.0/security/alerts” and “https://graph.microsoft.com/v1.0/security/incidents” that was discussed in the last blog post.
Connecting to your tenant
Once you know the command you want to run, you will first need to connect to your tenant. I talked about this a bit in the last post but to connect you will need to run “Connect-MgGraph”.
If you run this for the first time, after you authenticate you may get an Authentication Error. Usually this is due to not having the proper scopes. The scopes will specify what permissions you are requesting when you are logging in.
How do you know what scopes you need to specify? The “Microsoft.Graph.Security” provides a command called “Find-MGGraphCommand” that will show you the needed scopes. For example, if I need to find the scopes needed to retrieve alerts, I can run:
Find-MgGraphCommand -Command Get-MgSecurityAlert | Select -First 1 -ExpandProperty Permissions
This will return the following values (after converted to JSON)
[
{
"Name": "SecurityEvents.Read.All",
"IsAdmin": false,
"Description": "Read your organization’s security events",
"FullDescription": "Allows the app to read your organization’s security events without a signed-in user.",
"IsLeastPrivilege": true,
"PermissionType": "Application"
},
{
"Name": "SecurityEvents.ReadWrite.All",
"IsAdmin": false,
"Description": "Read and update your organization’s security events",
"FullDescription": "Allows the app to read your organization’s security events without a signed-in user. Also allows the app to update editable properties in security events.",
"IsLeastPrivilege": false,
"PermissionType": "Application"
}
]
So, you can see that the “SecurityEvents.Read.All” and “SecurityEvents.ReadWrite.All” will be required. If you run the command for the “Get-MgSecurityIncident” command, you will see that you will need the “SecurityIncident.Read.All” and “SecurityIncident.ReadWrite.All” scopes.
With this in mind, if I want to make a call to “Get-MgSecurityIncident”, I will need to run:
Connect-MgGraph -Scopes "SecurityIncident.Read.All","SecurityIncident.ReadWrite.All"
If you have not already granted the ability to use these scopes to your account, you will be presented with a dialog box asking permission to grant these rights. It is possible your administrator has already done this on your behalf but don’t get alarmed if you see the dialog box.
Running the command
Now that we are connected, we can start running the commands. I am interested in the incidents so I can run:
Get-MgSecurityIncident
This will return a list of ALL the incidents in my environment. For the purpose of this blog, I will only show the first entry (converted into JSON)
{
"Alerts": null,
"AssignedTo": "GaryBushey@GABTest419.onmicrosoft.com",
"Classification": "truePositive",
"Comments": [],
"CreatedDateTime": "2025-01-11T21:31:12.6Z",
"CustomTags": [],
"Description": null,
"Determination": "malware",
"DisplayName": "Incident from Archie Dog on multiple endpoints",
"Id": "12",
"IncidentWebUrl": "https://security.microsoft.com/incidents/12?tid=96229911-df4d-45c2-9405-73ab8f96cafd",
"LastModifiedBy": "User-GaryBushey@GABTest419.onmicrosoft.com",
"LastUpdateDateTime": "2025-01-12T17:35:51.4033333Z",
"RedirectIncidentId": null,
"ResolvingComment": "",
"Severity": "medium",
"Status": "inProgress",
"Summary": null,
"SystemTags": [],
"TenantId": "96229911-df4d-45c2-9405-73ab8f96cafd",
"AdditionalProperties": {}
}
If you compare this to what was returned when using the Microsoft Graph Explorer, you will the values are identical, if the order of the items is slightly different.
One thing I showed in the other blog is the ability to add “$expand=alerts” to the Microsoft Graph API URL to get the alerts returned with the incident. Good news! You can do this with the PowerShell command as well:
Get-MgSecurityIncident -ExpandProperty alerts
This will return the following which, again, is the same what was returned when calling the URL directly.
{
"Alerts": [
{
"ActorDisplayName": null,
"AdditionalData": {},
"AlertPolicyId": "a41f694c-c0f9-4c59-a069-2040601eeffc",
"AlertWebUrl": "https://security.microsoft.com/alerts/sn3ffcc4d5-cbca-480d-9ff2-acd834bfee98?tid=96229911-df4d-45c2-9405-73ab8f96cafd",
"AssignedTo": "GaryBushey@GABTest419.onmicrosoft.com",
"Category": "CredentialAccess",
"Classification": "truePositive",
"Comments": [],
"CreatedDateTime": "2025-01-11T21:31:12.4733333Z",
"Description": "Archie Dog's computer, Computer 2 , created an incident on 2025-01-10T00:00:00.0000000Z ",
"DetectionSource": "scheduledAlerts",
"DetectorId": "f2801fde-c623-4892-af2a-4bb324457f3a_a41f694c-c0f9-4c59-a069-204",
"Determination": "malware",
"Evidence": [
{
"CreatedDateTime": "2025-01-11T21:31:12.5433333Z",
"DetailedRoles": [],
"RemediationStatus": "none",
"RemediationStatusDetails": null,
"Roles": [],
"Tags": [],
"Verdict": "unknown"
},
{
"CreatedDateTime": "2025-01-11T21:31:12.5433333Z",
"DetailedRoles": [],
"RemediationStatus": "none",
"RemediationStatusDetails": null,
"Roles": [],
"Tags": [],
"Verdict": "unknown"
},
{
"CreatedDateTime": "2025-01-11T21:31:12.5433333Z",
"DetailedRoles": [],
"RemediationStatus": "none",
"RemediationStatusDetails": null,
"Roles": [],
"Tags": [],
"Verdict": "unknown"
},
{
"CreatedDateTime": "2025-01-11T21:31:12.5433333Z",
"DetailedRoles": [],
"RemediationStatus": "none",
"RemediationStatusDetails": null,
"Roles": [],
"Tags": [],
"Verdict": "unknown"
},
{
"CreatedDateTime": "2025-01-11T21:31:12.5433333Z",
"DetailedRoles": [],
"RemediationStatus": "none",
"RemediationStatusDetails": null,
"Roles": [],
"Tags": [],
"Verdict": "unknown"
},
{
"CreatedDateTime": "2025-01-11T21:31:12.5433333Z",
"DetailedRoles": [],
"RemediationStatus": "none",
"RemediationStatusDetails": null,
"Roles": [],
"Tags": [],
"Verdict": "unknown"
}
],
"FirstActivityDateTime": "2025-01-11T21:20:54.4071694Z",
"Id": "sn3ffcc4d5-cbca-480d-9ff2-acd834bfee98",
"IncidentId": "12",
"IncidentWebUrl": "https://security.microsoft.com/incidents/12?tid=96229911-df4d-45c2-9405-73ab8f96cafd",
"LastActivityDateTime": "2025-01-11T21:25:54.4071694Z",
"LastUpdateDateTime": "2025-01-12T17:35:51.39Z",
"MitreTechniques": [
"T1589",
"T0820",
"T1003",
"T1589.003",
"T1003.001"
],
"ProductName": "Microsoft Sentinel",
"ProviderAlertId": "3ffcc4d5-cbca-480d-9ff2-acd834bfee98",
"RecommendedActions": "",
"ResolvedDateTime": null,
"ServiceSource": "microsoftSentinel",
"Severity": "medium",
"Status": "inProgress",
"SystemTags": [],
"TenantId": "96229911-df4d-45c2-9405-73ab8f96cafd",
"ThreatDisplayName": null,
"ThreatFamilyName": null,
"Title": "Incident from Archie Dog "
}
],
"AssignedTo": "GaryBushey@GABTest419.onmicrosoft.com",
"Classification": "truePositive",
"Comments": [],
"CreatedDateTime": "2025-01-11T21:31:12.6Z",
"CustomTags": [],
"Description": null,
"Determination": "malware",
"DisplayName": "Incident from Archie Dog on multiple endpoints",
"Id": "12",
"IncidentWebUrl": "https://security.microsoft.com/incidents/12?tid=96229911-df4d-45c2-9405-73ab8f96cafd",
"LastModifiedBy": "User-GaryBushey@GABTest419.onmicrosoft.com",
"LastUpdateDateTime": "2025-01-12T17:35:51.4033333Z",
"RedirectIncidentId": null,
"ResolvingComment": "",
"Severity": "medium",
"Status": "inProgress",
"Summary": null,
"SystemTags": [],
"TenantId": "96229911-df4d-45c2-9405-73ab8f96cafd",
"AdditionalProperties": {}
}
Basically, any parameter that you could add to the URL, you can add as a property to the PowerShell command. I could also add “-Top=1” to the PowerShell command to show just the first entry (which I actually did behind the scenes)
Summary
It seems that using Microsoft Graph is the way to go in the future, so this post shows you how to access this using the PowerShell commands. It also goes into more details about how to connect to your environment while asking for the needed scopes. Finally, it shows an example of how to use one of the commands.
I highly recommend using the Microsoft Graph Explorer web site, Graph Explorer | Try Microsoft Graph APIs – Microsoft Graph, to play around with commands and then use the “Code Snippets” tab in the results section to see what the corresponding PowerShell command(s) will be used.