Powershell API Calls to Create Tags Failing with no Error Message

Copper Contributor

I created a PowerShell script that calls the Security Center Defender for Endpoint api to create a machine tag.  I am experiencing some irregular behavior.  I will make the call, and it will return a success code of 200 and my expectation is the tag has been created.  When I go into Security Center for Defender for Endpoint, I may not see the tag.  I could run the same script again, and the tag will be there.  I am making sure to stay within the API limitations of 100 calls per minute, 1500 calls per hour.  I don't know what is wrong.  Any ideas?  Here is a snippet:

 

$myurl = "https://api-us.securitycenter.windows.com/api/machines/" + $machine.id + "/tags"

$body =
@{
   "Value" = $tag
    “Action” = "Add"
}

# Send the webrequest and get the results.
$response = Invoke-WebRequest -Method Post -Uri $myurl -Body ($body | ConvertTo-Json) -Headers $headers -ErrorAction Stop
if ($response.StatusCode -eq 200)
{
   Write-Host "Added tag $tag to " $machine.computerDnsName

}

7 Replies
I tag a lot of machines with the API and haven't had any real issues.
It might be possible that there is some delay between when you add it through the API and when it's visible?
Have you tried to check through the API is you see the tag there?

@Thijs Lecomte 

I do not see the tags even after waiting a day.  They are really not there.  The problem occurs both when adding and removing tags.  I get a 200 response, indicating everything was OK, but then the tag was not added or removed as expected.  I guess I could do another call to see if the tag is there, but it wastes a lot of API calls.

It should work. I would advise you to open a support case to check what's going on

@Thijs Lecomte I will be opening a support case, thank you.

Hi,
@bellinghamlady 

 

Thanks for sharing your question with us!

 

In general, you’ll need to take the following steps to use the APIs:

  • Create an AAD application
  • Get an access token using this application
  • Use the token to access Defender for Endpoint API

I believe the API endpoint may be expecting a token to be sent with each request.

 

You must also add an app registration on the Azure AD tenant and give it permissions to authenticate with the API endpoint.

 

Can you post a screenshot of the output in your IDE? If there is a 200 code coming back from the endpoint then it doesn't necessarily indicate an authentication issue of course. 

 

I'm currently writing an application which is doing a similar thing, here is how I'm doing it - you could try it this way and see if the behaviour changes:

 

$tenantId = '' ### Paste your tenant ID (Azure AD Tenant ID) here
$appId = '' ### Paste your Application ID here
$appSecret = '' ### Paste your Application key here

$authBody = [Ordered] @{
    resource = "$resourceAppIdUri"
    client_id = "$appId"
    client_secret = "$appSecret"
    grant_type = 'client_credentials'
}

Write-Host "Authenticating..."
$authResponse = Invoke-RestMethod -Method Post -Uri $oAuthUri -Body $authBody -ErrorAction Stop

#Setting up a new variable which is referencing the access token attribute inside the authResponse.
Write-Host "Generating Token..."
$token = $authResponse.access_token

#This is our token object which we include in the web request as a JSON construct.
$HeaderToken = @{ "Authorization" = "Bearer $token" }

#Now we have our token generated, we can create our request to change the tags and send it to the API endpoint

$url = 'https://api.securitycenter.windows.com/api/machines'
$JSONTAG = [Ordered] @{ Action = "Add" ; Value = "#######"} | ConvertTo-Json -Compress
$request = Invoke-WebRequest -Method POST -Uri $url -Header $HeaderToken -body $JSONTAG

 

You can also use the Microsoft authentication library to generate credentials for the user who is running the script, that requires an additional PowerShell module to be imported and a different authentication function in the script.

Within Azure AD, you also have various options to authenticate - I personally find the above method works well especially if I want to abstract the permissions in the token I'm generating from the user or service who is running the script, which can then be controlled in Azure AD.

Further documentation:
https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-atp/apis-intr...

Let us know if this helps at all and if there are any further questions,

Best regards,

@Emc  Thank you for your reply.  To be clear, I am actually adding and removing tags to a single machine one by one.  So, my URL looks more like:

 

$url = "https://api-us.securitycenter.windows.com/api/machines/" + $machine.id + "/tags"

 

For some reason, I have to use api-us in place of api.

 

I am getting a good token and most of the api calls return 200, successful.  Occasionally, I get a timeout error.  I am refreshing the token every 30 minutes, making sure I only do 100 calls per minute and 1500 per hour.  A significant number of calls, however, return a code of 200, successful, but then I do not see the tag added or removed.  It is totally random.  I can run the same script twice, and one time it will say the tag has been created, but it is not there, and the second time I run it and it says the tag is there, and it really is there.  Again, my problem is intermittent.  I tried adding a 2 second delay between each api call, still no luck.  I did try your slightly different syntax for the header and body, but it did not seem to make a difference.  It is very frustrating, because the failure to create and remove tags is totally random.

@bellinghamlady 

Ok interesting - thank you for the clarification.

 

The geo of the endpoint should be fine either way.

 

Interestingly, I also noticed some similar anomalies in my own requests., but I think my problem was slightly different from yours.

For example, I would have a web request inside a conditional statement to filter for a specific set of machines in a domain, and add a tag to them. However, seemingly randomly, machines would be left out of the expected dataset when compared to the manual export, even although all requests were within the same loop function  - that being said, all machines in my dataset always had their tag added successfully by the script, despite outliers being left out which could indicate some trouble with the API itself. I'm not sure, I need to debug this particular issue more.

Upon running the script multiple times, the outliers would eventually be picked up which further may exclude possibility the script itself is buggy.

 

For clarification, I was working with small sets of requests of less than 50 machines each time, so no possibility of reaching the request limit.