This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Community Hub.
This blog is part two of a three-part series focused on facilitating programmatic data pulls from Microsoft APIs.
In part one of this series we discussed how an organization can leverage an Azure AD application registration and OAuth authentication to allow API access to alerts, incidents, and data in Microsoft 365 Defender and Microsoft Defender for Endpoint. This API access can enable programmatic Advanced Hunting queries and data pulls to improve hunting consistency, efficiency, speed, and completeness.
We specifically provided a high-level overview of the general methodology that can be used, broken down into 5 main steps, and provided details on how one can prepare an Azure AD OAuth app registration. In this second part of the series, we will cover steps 2 and 3, while in part three we will cover steps 4 and 5.
- Part 1:
- Step 1: Prepare an Azure AD OAuth app registration
- Part 2 (this blog):
- Step 2: Get an access token from the appropriate authorization for the API resource you intend to access
- Step 3: Provide the access token in a request to the API resource endpoint
- Part 3:
- Step 4: Retrieve and parse the results
- Step 5: Store the results for analysis
This blog will provide examples for leveraging PowerShell and C# code in Azure Functions. The same C# code can be used for a desktop app or a web API according to your need. Specifically our examples here will focus on the new Microsoft Graph APIs that were released for Public Preview that are documented in Use the Microsoft Graph security API - Microsoft Graph beta and discussed in the blog about the new Microsoft 365 Defender APIs in Microsoft Graph available in public preview.
Step 2 – Get an access token from the appropriate authorization for the API resource you intend to access
After you create your OAuth app in the tenant you wish the access, successfully configure the necessary permissions, and establish a way to authenticate (i.e., setup a certificate or a secret), the second step is to authenticate against the OAuth app to get an access token you can use to access the API resource.
2.1 Access your credentials
In part one of the series when we covered step “1.3 Configure Credentials”, which discusses how you can configure either a certificate or a secret for authentication. We also recommended leveraging an Azure Key Vault to securely store the key material.
Regardless of how you stored the credentials, however, the next thing you need to do is ensure your application accesses the stored credentials. Possible methods include using PowerShell to retrieve a certificate or secret from an Azure Key Vault, using C# to access a certificate or a secret from an Azure Key Vault, or even manually passing in a secret during code execution as a parameter.
2.1.1 Example option 1: Using PowerShell to retrieve a secret from an Azure Key Vault using Az.KeyVault module
One method is to leverage PowerShell to directly retrieve a secret using a command from the Az.KeyVault module.
What you will need:
- The name under which you stored the secret in the Azure Key Vault.
- The Az PowerShell module installed.
Once you have this, ultimately this is the command you will need to get executed:
If you store the output to a variable named “$appSecret”, you can then access the data you need via “$appSecret.SecretValueText”. Here is a screenshot example:
2.1.2 Example option 2: Using PowerShell and Azure CLI to retrieve a secret from an Azure Key Vault
A second method is to leverage PowerShell to call the Azure CLI to retrieve a secret.
What you will need: The name under which you stored the secret in the Azure Key Vault. Once you have this, ultimately this is the command you will need to get executed:
However, to script it out in PowerShell you can do something like this:
- Execute “az login”
- If successful, execute “az keyvault secret show” and store the results to a variable
- The result is a JSON output and what you need is the “value” property value
There is a detailed PowerShell script example below. The “keyVaultName” variable is the name of the key vault and “secretName” is the name under which you stored the secret value.
When executed interactively, the code above will open up a web browser to perform the authentication using the currently logged-in user. If you use the example above, elsewhere in your script you would access the secret value with “$appsecret”.
2.1.3 Example option 3: Using C# to retrieve a certificate from an Azure Key Vault
If you choose key vault certificates as the authorization mechanism, the code below can be used as an example for how to get the certificate from the Azure Key Vault using C#.
2.2 Authenticate as the OAuth app using the credential to get an access token
Once we have what we need to authenticate as the OAuth app, the next step is to generate a request to the authentication endpoint to get an access token.
2.2.1 Data you need
In order to get an access token you will need several pieces of information. If you are using a Microsoft Security API to access data in an Azure Tenant, you will need:
- The Azure “Directory (tenant) ID” (i.e., as identified in step 1.1 step 5 from blog one)
- The OAuth Application (client) ID (i.e., as identified in step 1.1 step 5 from blog one)
- The URI of the Authentication Endpoint for the Azure Tenant
- For Azure AD Public the URI base is “login.microsoftonline.com” (e.g., most tenants and GCC)
- For Azure AD Government the URI base is “login.microsoftonline.us” (i.e., GCC-High)
- For other clouds see Azure AD authentication & national clouds - Microsoft Entra | Microsoft Docs
- The base URI of the resource you are trying to access
- For Microsoft Graph APIs:
- Commercial: graph.microsoft.com
- GCC: graph.microsoft.com
- Microsoft Graph for US Government L4: graph.microsoft.us
- Microsoft Graph for US Government L5 (DOD): dod-graph.microsoft.us
- Others: see the URL above
- For Microsoft 365 Defender APIs:
- Commercial: api.security.microsoft.com
- GCC: api-gcc.security.microsoft.us
- GCC High & DoD: api-gov.security.microsoft.us
- For Microsoft Defender for Endpoint APIs
- Commercial: api.securitycenter.microsoft.com
- GCC: api-gcc.securitycenter.microsoft.us
- GCC High & DoD: api-gov.securitycenter.microsoft.us
- For Microsoft Graph APIs:
Depending upon which resource you need, you may most often need the “Graph” API endpoints.
2.1.2 Don’t know if you should use the Public or Government authentication endpoint?
If you’re not sure if you need to use the Authentication Endpoint for “Azure AD Government” you can follow the instructions at Azure AD authentication & national clouds - Microsoft Entra | Microsoft Docs > Frequently asked questions > How do I identify an Azure Government tenant?.
To summarize, you must do the following:
- Navigate in a web browser to “https://login.microsoftonline.com/<domainname>/.well-known/openid-configuration”, where “<domainname> is the default domain name of the Azure AD tenant. You can also use the Tenant ID (i.e., “Directory (tenant) ID” from the OAuth app overview page) instead of the domain name
- Look for the “tenant_region_scope” value
- If the value is USG or USGov, you have an Azure Government tenant; thus, you should use the “Azure AD Government” authentication endpoint.
- The tenant_region_scope property is exactly how it sounds, regional. If you have a tenant in Azure Public in North America, the value would be NA; if the value is not USG or USGov then this indicates an Azure Public region, and thus you should use the “Azure AD Public” authentication endpoint
- If you have a “tenant_region_sub_scope” of “GCC” this does NOT mean you use the “Azure AD Government” endpoint – in this case you would still use the “Azure AD Public” authentication endpoint
2.2.3 Example option 1: Using PowerShell to get an access token
Ultimately to use PowerShell & an application secret to get an access token regardless of whether you want to access Microsoft Graph, Microsoft 365 Defender, or Microsoft Defender for Endpoint APIs the important piece of code is going to boil down to this PowerShell code:
In the above example, for which I’ll give more complete code shortly:
- the “$resourceBaseUri” variable represents item 4 from step 2.2.1
- the “$oAuthBaseUri” variable represents item 3 from step 2.2.1
- the “$TenantId” variable represents item 1 from step 2.2.1
- the “$ClientId” variable represents item 2 from step 2.2.1
- the “$ClientSecret” variable represents the secret retrieved in step 2.1.1
If you take the code snippet above you can flesh it out into a full-fledged PowerShell function that:
- takes parameters for the input variables
- includes switches to indicate Azure AD Public or Azure AD Government clouds
- includes switches to indicate Commercial or GCC resource endpoints
The below PowerShell code would get an access token specifically for Microsoft Graph:
When executed, it will look something like this:
As you can see in the example above, I stored the results in a variable named “$graphAccessToken”. This value will be used in future steps.
2.2.4 Example option 2: Use C# to get an access token
In this C# example we are using the certificate retrieved from the Key Vault to get access token. Use this method if you use certificate as the authorization method.
2.2.5 Troubleshooting / validation of access token using PowerShell
If you are curious about the details of the access token, one way you can validate the parameters is to decode the access token using PowerShell. While explanation of the inner workings of the access token are outside the scope of this blog, you can read more information about the structure of the access token at RFC 7519: JSON Web Token (JWT) (rfc-editor.org). For Azure Active Directory tokens you see full explanations of the exact property meanings at Microsoft identity platform access tokens - Microsoft Entra | Microsoft Docs.
The function below will accept an input parameter of the access token. If you set the output of the example PowerShell function shown in step 2.1.2 to a variable named “$token”, for example, the access token would be a property named “$token.access_token”.
The output of the function is an object with two properties: header and payload. The data we are most interested in is the “payload”, where we can see various pieces of information including the following:
- permissions that were granted (.payload.roles),
- the application id of the OAuth application that was authenticated against (.payload.appid),
- the application display name (.payload.app_displayname),
- the tenant id the authentication was for (.payload.tid),
- the expiration time in UTC (.payload.exp)
Two pieces of data of particular interest are the token expiration date (exp) and the permissions (roles) assigned. Additionally, you can see that this access token is valid for the endpoint (aud, “audience”) https://graph.microsoft.com.
In the example above you can see this access token was granted the roles “SecurityEvents.Read.All”, “SecurityAlert.Read.All”, “SecurityINcident.Read.All”, and “ThreatHunting.Read.All”. We can use this to confirm that we have the “SecurityAlert.Read.All” permission necessary to use the List alerts_v2 - Microsoft Graph beta | Microsoft Learn API and the “SecurityIncident.Read.All” permission necessary to use the List incidents - Microsoft Graph beta | Microsoft Learn API, for example.
Step 3 – Submit request to API resource endpoint
Once we have an access token, we can submit the access token to the API resource endpoint (i.e., the “audience”).
3.1 Data you need
To submit the request to the API endpoint you basically only need to know five things, at least for a Microsoft security API:
- The access token you received from step 2
- The base URI of the resource you are trying to access (i.e., item 4 from step 2.2.1)
- Example: graph.microsoft.com
- The API version you will be calling
- Note: This will usually be “v1.0” for most production Microsoft APIs or “beta”
- Example: /beta if you are using List alerts_v2 - Microsoft Graph beta | Microsoft Learn
- The path component of the specific resource you want to access
- Example: /security/alerts_v2 if you are using List alerts_v2 - Microsoft Graph beta | Microsoft Learn
- Any optional query parameters you may want to use to modify the data returned from the API
- Note: The API documentation will list which parameters are available for the specific API you will be calling
- Note: this is optional and does not need to be specified
- Example: ?$top=100&$skip=200 if you want to only get 100 records, but skip the first 200 records
Identifying these pieces of information is the most step that needs to be completed before we can successfully send a request to the API endpoint. The actual submission is often relatively straightforward.
If we put all the example pieces of information together from items 2 – 5 above, we would get this full, example URI:
If we request data from that API URI we would get a list of 100 alerts from Microsoft 365 Defender, as accessed from Microsoft Graph, after the first 200 alerts were skipped.
3.2 Example option 1: Use PowerShell to submit the API request
The best way to use PowerShell to submit a request to an API is to use the “Invoke-WebRequest” command, where there are three main parameters that need to be set:
- Method (e.g., Get)
- URI (i.e., the full Uniform Resource Identifier that specifies what API you are accessing, complete with all parameters, as joined together from items 2-5 from step 3.1 above)
- Headers (i.e., the web header for the request, which includes the access token as identified in item 1 from step 3.1 above)
Assuming we are continuing to use our example of the List alerts_v2 - Microsoft Graph beta | Microsoft Learn API, the URI we will be using is the full example provided in step 3.1, and we have our access token stored in a variable named $graphAccessToken as grabbed in step 2.1.1, this PowerShell code sample will do the job:
That same format can be used for any Microsoft 365 Defender APIs, Microsoft Defender for Endpoint APIs, or Microsoft Graph API endpoints. When that code snippet is executed, the results will be stored in the “$gwebResponse” variable, with the actual data stored as JSON in the “content property (i.e., “$gwebResponse.Content”). The third and final part of this blog series will cover more details on how to retrieve and process the response data.
Below is a more fully-fledged script that increases the complexity of the code but drastically simplifies the execution:
A line-by-line review of the script is outside of the scope of this blog, but in summary:
- It enables parameters to
- set a “skip” integer to skip N number of records
- set a “top” integer to return N number of records
- set a “select” string to choose which columns are returned in the data (e.g., “id,title,createdDateTime”)
- set an “orderBy” string to sort the data based upon a column (e.g., “createdDateTime desc”)
- choose an option to return “all” records without any filters
- choose options to simply filter records based upon a createdDateTime value (either “createdAfter” a specific date or “createdBefore” a specific date) or a lastUpdateDateTime (either “updatedAfter” a specific date or “updatedBefore” a specific date)
- choose to provide a manually specified filter if the simple filter options aren’t sufficient
- It dynamically extracts the API base URL based upon the access token
- It dynamically generates the API URI, including the query parameters, based upon the parameters inputted into the script
Assuming the script above is saved to C:\temp\OauthAlertsV2.ps1, this command would return the id, title, and createdDateTime values for the most recent 2 alerts:
The above screenshot shows what a normal, unparsed response would look like.
3.3 Example option 2: C# example
Below is the way to submit the same API request but using C#.
3.3.1 API Response Class
Create a re-usable class in your project for holding deserialized responses.
3.3.2 Setup Components
Create a class and members used to invoke the request. Pay particular attention that HttpClient is intended to be instantiated once in the constructor and reused throughout its lifetime.
3.3.3 API Query Method
Create a method for building the filter, posting the request, and returning a response.
In this second blog of a planned three-part series, we discussed steps 2 and 3 of the five broad steps for facilitating programmatic data pulls from Microsoft APIs, how to get an access token for the API resource and how to provide that access token to the API resource. The overview of the methodology and step 1, how to prepare an Azure AD OAuth application registration, was covered in part one.
At this point you should understand:
- … why leveraging an OAuth application to pull security information via an API can be beneficial
- … how the overall methodology of how the OAuth application can be leveraged,
- … how to configure the OAuth application to have the necessary permissions
- … how to configure access to the OAuth application
- … how to successfully authenticate to the OAuth application and get an access token
- … how to use the access token to submit a request to an API resource endpoint
In our third and final blog in the series, we will discuss step 4 and step 5, how to review and parse the results and store the results for analysis.