Once in a while, I’m receiving questions related to MDE extension deployment on servers and especially on how the activity status can be monitored. Even though this is more of an operational-related issue I decided to write a short blog post about the topic. From this blog you can expect to find ways to monitor the status with Azure Workbooks as well as Azure Resource Graph & KQL queries to build alert rules in cases it’s needed.

Background – What’s MDE Extension?

When you enable Defender for Cloud (MDC) enhanced security plan, also known as Defender plans, for Servers, the MDC automatically triggers MDE extension deployment on all servers in the scope. To establish the deployment, integration between MDC – MDE needs to be enabled (and also a Unified experience enabled to cover all servers).

Extensions:

  • MDE.Windows for Windows Servers
  • MDE.Linux for Linux Servers

All the servers need to fulfill the minimum requirements for Microsoft Defender for Endpoint. Instructions on how to take the most out of the MDC-MDE integration and how to deploy – Protect endpoints with MDC integrated EDR solution – Microsoft Defender for Endpoint.

Where to Find Audit Events Related to MDE Extensions Deployment?

In a nutshell, the data is found in two places, Azure Resource Graph & Azure Activity Log. Which one to use depends on what you would like to achieve. Let’s look at the scenarios in the following chapters.

Azure Resource Graph (ARG)

The Azure Resource Graph provides an ability to explore Azure resources at scale across the environment.

Azure Machines

If you would like to find MDE extension-related data (MDE.Windows & MDE.Linux) from Azure machines the data is stored underneath ‘microsoft.compute’ provider, the full path being ‘microsoft.compute/virtualmachines/extensions’.

resources
| where type == 'microsoft.compute/virtualmachines'
| extend resourceId = tolower(id)
| join kind = leftouter (resources
| where type == 'microsoft.compute/virtualmachines/extensions' and name has 'MDE'
| extend resourceId = tolower((split(id, "/extensions"))[0]), state = properties.provisioningState
) on resourceId
| project name, location, resourceGroup, name1, ['state']

Azure Arc Machines

If you would like to find MDE extension-related data (MDE.Windows & MDE.Linux) from Azure Arc machines the data is stored underneath a different provider and, you need to use ‘microsoft.hybridcompute/machines’ as a provider. The full path is ‘microsoft.hybridcompute/machines/extensions’.

//Find MDE extension status from Arc Machines
resources
| where type == "microsoft.hybridcompute/machines"
| extend resourceId = tolower(id)
| join kind = leftouter (resources
| where type == 'microsoft.hybridcompute/machines/extensions' and name has 'MDE'
| extend resourceId = tolower((split(id, "/extensions"))[0]), state = properties.provisioningState
) on resourceId
| project name, location, resourceGroup, name1, ['state']

Azure Workbooks

There are two (2) workbooks I’m using for the monitoring of MDE extension deployments or when I need the status of the current situation. The first one is made by my colleague Markus Pitkaranta. The workbook covers a lot more than only MDE status and provides very useful insight into different agents’ deployment and their overall status on servers (Azure / Arc).

The workbook can be found on Markus’s GitHub repo – Microsoft Sentinel Workbooks.

If you are in the middle of MDE deployment (or starting one) and would like to monitor the deployment status here is a great addition from Microsoft Product Group to monitor the installation status – Microsoft Defender for Endpoint Status.

KQL Queries

Last but not least, you can use Azure Activity Log raw data ingested to Azure Log Analytics workspace. This raw data can be used to create an alert from the activity. The use case I’d on my table and was the main drive to write this blog – ‘I would like to receive an alert every time when MDE extension installation is failed’.

The alert can be created in Microsoft Sentinel or Azure Monitor. In this scenario, I would prefer Azure Monitor because this use case is more close to operational issues than offense and Security Operation Center might not be the correct place for investigating this kind of alert.

The Azure Activity Log has the data from the management plane as can see below.

Here are KQL queries that can be used as a starter to detect installation status. The following queries are found in the next chapter:

  • Find out MDE.Windows extension where installation status is ‘Failure’
  • Find out MDE.Windows extension where installation status is ‘Success’
  • Find out MDE.Linux extension where installation status is ‘Failure’
  • Find out MDE.Windows extension where installation status is ‘Failure’ in Azure Arc machines

MDE.Windows – Azure Machines

//Find out MDE.Windows extension where installation status is 'Failure'
AzureActivity
| where CategoryValue == 'Administrative'
| where ResourceProviderValue contains "MICROSOFT.COMPUTE"
| extend ['Scope'] = tostring(parse_json(Authorization).scope)
| extend ['ErrorMessage'] = tostring(parse_json(tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).statusMessage)).error)).details))[0].message)
| extend ['Error Code'] = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).statusMessage)).error)).code)
| extend ['ResourceName'] = tostring(Properties_d.resource)
| extend ['Extension'] = split(['ResourceName'],'/')[0]
| extend ['VM'] = split(['ResourceName'],'/')[1]
| where ErrorMessage contains "Failed to configure Microsoft Defender for Endpoint: Onboarding to MDE via Microsoft Defender for Cloud"
| where ActivityStatusValue contains "Failure"
| where Scope contains "MDE.Windows"
| project TimeGenerated, SubscriptionId, ResourceGroup, ActivityStatus, CallerIpAddress, Caller, ['ErrorMessage'], ['Error Code'], ['VM'], ['Extension']

//Find out MDE.Windows extension where installation status is 'Success'
AzureActivity
| where CategoryValue == 'Administrative'
| where ResourceProviderValue contains "MICROSOFT.COMPUTE"
| extend message_ = tostring(parse_json(Properties).message)
| extend ['Scope'] = tostring(parse_json(Authorization).scope)
| extend ['ResourceName'] = tostring(Properties_d.resource)
| extend ['Extension'] = split(['ResourceName'],'/')[0]
| extend ['VM'] = split(['ResourceName'],'/')[1]
| where message_ contains "Microsoft.Compute/virtualMachines/extensions/write"
| where ActivityStatusValue contains "Success"
| where ['Scope'] contains "MDE.Windows"
| project TimeGenerated, SubscriptionId, ResourceGroup, ActivityStatus, CallerIpAddress, Caller, ['VM'], ['Extension']

Azure Arc Machines

//Find out Azure Arc connected Windows machines where MDE extension installation status is failed
AzureActivity
| where CategoryValue == 'Administrative'
| where ResourceProviderValue contains "MICROSOFT.HYBRIDCOMPUTE"
| extend ['Scope'] = tostring(parse_json(Authorization).scope)
| extend ['ErrorMessage'] = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).statusMessage)).error)).message)
| extend ['Error Code'] = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).statusMessage)).error)).code)
| extend ['ResourceName'] = tostring(Properties_d.resource)
| extend ['VMName'] = split(['ResourceName'],'/')[0]
| extend ['Extension'] = split(['ResourceName'],'/')[1]
| where ErrorMessage contains "The resource operation completed with terminal provisioning state 'Failed'"
| where ActivityStatusValue contains "Failure"
| where Scope contains "MDE.Windows"
| project TimeGenerated, ['ResourceName'], ['Resource'], ['ErrorMessage'], ['Error Code'], ['Scope'], SubscriptionId, CallerIpAddress, Caller

MDE.Linux

//Find out MDE.Linux extension success deployments
AzureActivity
| where CategoryValue == 'Administrative'
| where ResourceProviderValue contains "MICROSOFT.COMPUTE"
| extend ['Scope'] = tostring(parse_json(Authorization).scope)
| extend ['Error Code'] = tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).statusMessage)).error)).code)
| extend ['ResourceName'] = tostring(Properties_d.resource)
| extend ['Extension'] = split(['ResourceName'],'/')[0]
| extend ['VM'] = split(['ResourceName'],'/')[1]
| where ActivityStatusValue contains "Success"
| where ['Scope'] contains "MDE.Linux"
| project TimeGenerated, SubscriptionId, ResourceGroup, ActivityStatus, CallerIpAddress, Caller, ['VM'], ['Extension']

Summary

If you would like to create alerting functionality such as my community member, you can do it with Azure Monitor or Microsoft Sentinel. That part is not covered by this blog.

All queries are found from my GitHub repository – Sentinel Queries/MDE Extension.

When the machine’s MDE extension status is known it’s time to identify the root cause. This article can be helpful and get you started with it:

References

Track MDE deployment status

Monitor Server’s agent status

Microsoft Defender for Servers – Defender for Endpoint Deployment Status