*5 Minutes

*Low complexity


Applying a security solution in an enterprise environment can be a complex endeavor. Customers deploy various layers of protection solutions, investigation platforms and hunting tools. Security Operation teams attempt to tackle this task, but typically lack expensive and experienced human resources to overcome this challenge. Automation is a decent mitigation but automating the security procedures and wiring the security components all together to a solid cyber security solution, requires programmatic access to each solution.


In these series of blogs, we will walk you through common automation scenarios that you can achieve with Windows Defender ATP to optimize workflows. For more information on Windows Defender ATP APIs, see the full documentation.


We called this blog “Hello World” as every long software journey starts with a simple step. We’ll show you how to programmatically extract Windows Defender ATP alerts with a PowerShell script. You can schedule this script to run on any machine and you may modify it to use the alert information in your specific use case. We can imagine a handful of standard use cases where a Security Operations Center (SOC) can leverage this basic capability.


Some scenarios where this can be applied include use with security information and event management (SIEM) connectors, ticketing systems, and security orchestration and response (SOAR) solutions. SIEM connectors may be the simplest example while ticketing systems are a common one, and SOAR solutions may be a complex use case.


So let gets started….


How long it takes to go through this example?


It only takes 5 minutes done in two steps:

  • Application registration: takes 2 minutes
  • Use examples: only requires copy/paste of a short PowerShell script

Do I need a permission to connect?

For the app registration stage, you must have a Global administrator role in your Azure Active Directory (Azure AD) tenant.


Step 1 - Register the app in Azure Active Directory


  • With your Global administrator credentials, login to the Azure portal.
    • Azure Active Directory > App registrations > New application registration.



  • In the registration form:
    • Name - Enter any name of choice.
    • Application type - Use “Web app / API”. We’ll discuss the other options on future blogs.
    • Sign-on URL – For this example you can copy the following value: https://www.ContosoWdatpConnector.com
    • Click Create.


  • The application we created is the authentication entity, just like a service account. We now need to set permissions to my app and save its credential for later use.
  • Settings > Required permissions > +Add



  • In the "Add API access" panel:
    • Select an API
    • Paste “WindowsDefenderATP” in the search box (it will not appear in the list).
    • Select “WindowsDefenderATP”
    • Click Select






  • In this example, we only need the “Read all alerts” permission.
  • Select the “Read all alerts” permission and click Select and
  • You may select more permissions for other scenarios.



  • Now we need to grant permission in the "Required permission" page. It is the tenant admin consent allowing the app to access the required permissions.
    • Click Grant permission


  • As explained, the registered app is an authentication entity with permission to access all alerts for reading. Now we need to get and store the authentication and authorization credentials:
    • Key (application secret), Application ID.
  • To get credentials:
    • Click Settings > Keys
    • Specify a key description (e.g. “ContosoWdatpApi”) with duration of 1 year.



  • Click Save.
  • The application key will appear.
    IMPORTANT: Copy and store this key in a safe place. Treat it like a password.
  • Application ID: copy and store the Application ID. This is the “user name” of the credentials. See the green frame above.


  • Finally, we need the “Tenant ID” (a.k.a Directory ID) to target the API to the correct tenant:
    • Go back to the main screen ( https://portal.azure.com)
    • Azure Active Directory > Properties
    • Copy and store the "Directory ID"




Done! You have successfully registered an application. You may reuse this application when going through the exercises that we’ll be using in future blogs and experiments.


Now we’ll need to connect the API which means getting a token. The token is proof for Windows Defender ATP that an API call is authenticated and authorized.



Connecting the API:

  • Copy the text below to PowerShell ISE or to a text editor.


# That code get the App Context Token and save it to file name "Latest-token.txt" under the current directory

# Paste below your Tenant ID, App ID and App Secret (App key).


$tenantId = 'ece8fbca-9f5a-4ca3-9ce3-52c1ca6eb4d7' ### Paste your own tenant ID here

$appId = '45097602-0cfe-4cc6-925f-9f453233e62c' ### Paste your own app ID here

$appSecret = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890=' ### Paste your own app keys here


$resourceAppIdUri = 'https://api.securitycenter.windows.com'

$oAuthUri = "https://login.windows.net/$TenantId/oauth2/token"

$authBody = [Ordered] @{

    resource = "$resourceAppIdUri"

    client_id = "$appId"

    client_secret = "$appSecret"

    grant_type = 'client_credentials'


$authResponse = Invoke-RestMethod -Method Post -Uri $oAuthUri -Body $authBody -ErrorAction Stop

$token = $authResponse.access_token

Out-File -FilePath "./Latest-token.txt" -InputObject $token

return $token


  • Save the script to file. You can name it "Get-Token.ps1".
  • Running this script by pressing F5 will get a token and save it in the working folder under the name "./Latest-token.txt".



If you are getting an error:

\Get-Token.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.

That error indicates that your Powershell execution policy not allowing you to run scripts. You can change the execution policy by running that command in Powershell console:

PS c:\>>Set-ExecutionPolicy unrestricted -Scope CurrentUser

Consider consulting with your system administrator about your organization’s Powershell execution policy.


Sanity Check

  • In your browser go to: https://jwt.ms/
  • Copy the token (the content of the Latest-token.txt file).
  • Paste in the top box.
  • Look for the "roles" section. Find the Alert.Read.All role.


  • Now let’s gets the alerts, Copy the following text to a new PowerShell Script.


# Returns Alerts created in the past 48 hours.


$token = ./Get-Token.ps1       #run the script Get-Token.ps1  - make sure you are running this script from the same folder of Get-Token.ps1


# Get Alert from the last 48 hours. Make sure you have alerts in that time frame.

$dateTime = (Get-Date).ToUniversalTime().AddHours(-48).ToString("o")      


# The URL contains the type of query and the time filter we create above

# Read more about other query options and filters here

$url = "https://api.securitycenter.windows.com/api/alerts?`$filter=alertCreationTime ge $dateTime"


# Set the WebRequest headers

$headers = @{

    'Content-Type' = 'application/json'

    Accept = 'application/json'

    Authorization = "Bearer $token"



# Send the webrequest and get the results. 

$response = Invoke-WebRequest -Method Get -Uri $url -Headers $headers -ErrorAction Stop


#Extract the alerts from the results. 

$alerts =  ($response | ConvertFrom-Json).value | ConvertTo-Json


#Get string with the execution time. We concatenate that string to the output file to avoid overwrite the file

$dateTimeForFileName = Get-Date -Format o | foreach {$_ -replace ":", "."}   


#save the result as json and as csv

$outputJsonPath = "./Latest Alerts $dateTimeForFileName.json"    

$outputCsvPath = "./Latest Alerts $dateTimeForFileName.csv"


Out-File -FilePath $outputJsonPath -InputObject $alerts

($alerts | ConvertFrom-Json) | Export-CSV $outputCsvPath -NoTypeInformation


  • Save the file in the same folder you saved the previous script (Get-Token.ps1).
  • You can run the script by right-clicking on the file and choosing "Run with PowerShell" or run it from PowerShell console.
    NOTE: If you are using powershell_ise.exe make sure to change directory to the directory with the scripts.


  • You will now see two files (json and csv) created in the same folder as the scripts.
  • The files are the latest alert from your tenant in the past 48 hours.


You’re all done! You have just successfully:

  • Created and registered and application
  • Granted permission for that application to read alerts
  • Connected the API
  • Use a PowerShell script to return alerts created in the past 48 hours



In the next blog, we’ll walk you through updating alert status programmatically. I invite you to suggest more use cases that you’d like for us to blog about, provide feedback, and ask questions about this post!



@Haim Goldshtein, security software engineer, WDATP

@Dan Michelson, program manager, WDATP



This maybe naive since I don't remote, but I never saw anything, as I scrolled through, about the "Defender" Module. It is a great Module and I believe it can be used remotely or at the vary least on a providing server in a small operation?

I us it all the time from Powershell ISE.

Thanks for your feedback.

Happy to hear that you are using the "Defender" module. The "Defender" module covers the AV management. Here, in this blog post, we are taking the first step in encouraging our customers to try and automate WDATP procedures. Will be great to get feedback about that too. We are here for your questions and feedback.



I get this back in the Token 

  "roles": [

but this from the Invoke

Invoke-WebRequest : The remote server returned an error: (401) Unauthorized.

Did I copy the pwd key wrong, I was sure I didn't... Thoughts Ideas?




From first look, it seems that the part of granting permissions was not completed successfully. Please jump to the line "In this example, we only need the “Read all alerts” permission." and try to follow the steps there. If you find that the desired read permission is missing, that might be the problem. Please, pay attention to the "grant permissions" step.

Hope this helps.


Hi Murray,


it looks like your token holds the right permission.

first, please pay attention that you save both the Get-token.ps1 and Get-Alert.ps1 in the same folder.

when you open the script with Powershell_ise.exe the default folder is the user folder (PS C:\Users\{Username}>) which means that if you run the script by pressing F5 the Powershell executer will try to find the file Get-Token.ps1 under your user profile. 

you need to change the running directory to the directory you saved the scripts or to call Get-Token.ps1 script by using its full path and not the relative path.


Hope it helps.



I went back in a validated my steps, I took a screen shot, and confirmed I am in the right directory when running the script, the token looks ok, - What license for Azure AD do I need to get the logging? AzureADApp.jpg


according to the token validation result you published, you don't have any problem with the permission. 

could you make sure that you are running the script while the powershell console running directory point to the folder with the scripts?

for example, if you saved the scripts under C:\temp\api then you need to make sure to change the directory in the powershell_ise running folder (you can use the dir command to check if you see the scripts) 




if you run the script from another folder the Get-Alert.ps1 script will not find the Get-Token.ps1 script and you will get an error message 401 (unauthorized) like you published in your first message.


Hope it helps.



AzureADAppTroubleshooting.jpgYes, the article made that clear so I even when down to the command line and ran the commands.  The token is getting generated, its the web response that is being tagged as unauth - I looked at the token and it looks fine.  Its why I was asking about the AAD License required.. 

Thanks for you help and responses on this matter - I understand this is just a training item and shouldn't be so hard to run - so the overall importance isn't great , just trying to figure out why its not working has me digging!


Great update and realization for me - seems counter intuitive for me to say this, but you obviously (or not so to me!) need to be licensed for ATP via M365 license prior to running the code, If you have a M365 E5 license or have ATP licenced you can run the code, or alternatively you can license ATP for a trial, here https://www.microsoft.com/en-us/windowsforbusiness/windows-atp 

Without being licensed for ATP you will not be authorized to run the demonstration!

Thanks and keep the posts coming - its time to license up some features to see all the great features


Frequent Visitor

I'm getting the same unauthorized message. If the application is the one obtaining the token that give you access to the API then how do you apply a license to that application? I have all appropriate licenses assigned to my admin account, and I still get the unauthorized message.


Hello, one of the troubleshooting steps I was asked to do first was to validate my ATP via this page here - 


to https://securitycenter.windows.com/preferences2/integration  This will validate your ATP license is good and you have the necessary access to run the script.  I did not as I do not have an E5 or M365 license with ATP enabled.  If you get the following graphic then I would investigate wNOAtp.jpgith your licensing team .  Let me know how you make out!

Frequent Visitor

I'm able to view everything in the portal. I'm still getting unauthorized when I execute the script. I gave my application all possible permissions to the defender api. I'll probably just open a ticket with MS.


To assist we need more details.

We'll contact you.



I was able to execute everything with no issues but when the results come back they are missing the machine name and the user name associated with the alert.  The results return the machine id but we are not sure how to get that converted to machine name.


We'll sort this out with you in the private channel and publish insights here if applicable.


Hey @tferrara ,


As you probably saw, the alert includes only machine id and if you want to enrich that data with machine name you need to call another API method Get machine by ID   which returns lots of information about the machine.

you can see in our second blog Ticketing system integration how we pull alerts and iterate through the alerts to use the alert id to update the ticketing system. you can use the same logic to get the machine information and enrich the alert. your script can look like:

# Returns Alerts created in the past 4 hours.
# Setting a place holder for a code to open a ticket in external ticketing system.

$token = .\Get-Token.ps1

$dateTime = (Get-Date).ToUniversalTime().AddHours(-400).ToString("o")

$url = "https://api.securitycenter.windows.com/api/alerts?`$filter=alertCreationTime ge $dateTime"

$headers = @{ 
    'Content-Type' = 'application/json'
    Accept = 'application/json'
    Authorization = "Bearer $token" 

$response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers -ErrorAction Stop

#foreach alert, get the machineId and alertId and isloate machine while writing the alert ID in the isolation comments. 

foreach ($alert in $response.value)
    $alertId = $alert.id
	$machineId = $alert.machineId
	$url = "https://api.securitycenter.windows.com/api/machines/$machineId" 
	$body = @{}

	$headers = @{		
		Authorization = "Bearer $token" 
	$machineInfoResponse = Invoke-WebRequest -Method Get -Uri $url -Body $body -Headers $headers -ErrorAction Stop 
	#check the isolatino request code and write to log file. 
	if($machineInfoResponse.StatusCode -eq 200) { 
        $machineInfo =  $machineInfoResponse | select -Expand Content | ConvertFrom-Json
        $machineName = $machineInfo.computerDnsName

		# replace the next line with your code to take the alerts data and enrich it with machine info.
        [System.Windows.MessageBox]::Show("Alert ID - $alertId. MachineName - $machineName")
	else { 
		[System.Windows.MessageBox]::Show("Failed to get machine info for machien id - $machineId")

We are working on a Powershell module which should ease the use of the API from Powershell.


Please reply if the answer answers your issue.



Perfect.. Thanks @Haim Goldshtein... One last question as I was going through all the ms docs on api's.  We came across https://wdatp-alertexporter-us.windows.com/api/alerts and when we try to call it we.  I'm assuming this link isn't valid anymore?

Authorization has been denied for this request