Plan and deploy Microsoft Defender for Identity sensors on Active Directory domain controllers, configure gMSA service accounts, verify sensor health, validate entity detection, and establish ongoing monitoring for your identity infrastructure.
Microsoft Defender for Identity (MDI) is a cloud-based security solution that leverages on-premises Active Directory signals to detect advanced threats, compromised identities, and malicious insider actions. MDI sensors run directly on domain controllers, capturing authentication traffic (Kerberos, NTLM), LDAP queries, DNS lookups, and directory replication events in real time. no port mirroring required. The sensor analyzes traffic to build behavioral profiles for every user, device, and service account, enabling detection of anomalous activities such as credential theft, lateral movement, and privilege escalation. This lab walks you through a complete enterprise deployment: verifying prerequisites, creating gMSA accounts, installing sensors, validating entity discovery, and establishing health monitoring.
Fabrikam Inc., a manufacturing company with 5,000 employees, operates a multi-site AD forest with 12 domain controllers across two domains. The security team has identified gaps in identity threat visibility: pass-the-hash attacks, Kerberoasting, and DCSync operations go undetected by traditional log monitoring. A recent penetration test revealed that an attacker could escalate from compromised workstation to domain admin within 4 hours without triggering alerts. The CISO has mandated real-time identity threat detection across all DCs within 30 days.
Success criteria: 100% DC coverage, all sensors healthy, entity profiles generated, lateral movement paths mapped, initial security assessments reviewed.
Active Directory is the backbone of enterprise identity: 95% of Fortune 1000 companies rely on AD for authentication and authorization. Identity-based attacks increased by 300% year-over-year according to Microsoft Digital Defense Report, with credential theft being the most common initial access vector. The average time to detect an identity compromise is 207 days without proper monitoring. MDI reduces this to minutes. Attacks like Pass-the-Hash, Golden Ticket, and DCSync are invisible to traditional SIEM solutions because they use legitimate protocols. MDI detects behavioral anomalies within these protocols. MDI provides the identity signal layer for Microsoft Defender XDR, enabling correlated incidents spanning identity, endpoint, email, and cloud app detections.
*.atp.azure.com endpoints*.atp.azure.com via DNSUnderstand the MDI deployment model before installing sensors. Sensors run as services on each DC, capturing authentication protocols, LDAP queries, DNS lookups, and directory replication traffic via Windows Event Tracing (ETW) and network parsing.
# WHAT: Inventory all domain controllers across every domain in the AD forest
# WHY: MDI sensors must be deployed on ALL DCs - even one uncovered DC is a detection blind spot.
# This script discovers DCs across all domains and sites so you can plan a phased rollout.
# OUTPUT: Table showing DC name, parent domain, AD site, OS version, and IP address
# Use this list to prioritize deployment order (start with pilot DCs, then cover all remaining)
$forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$forest.Domains | ForEach-Object {
$_.DomainControllers | Select-Object Name, Domain, SiteName, OSVersion,
@{N='IPAddress';E={[System.Net.Dns]::GetHostAddresses($_.Name).IPAddressToString -join ', '}}
} | Format-Table -AutoSizeRun prerequisite checks on each target DC to verify hardware resources, OS version, .NET Framework, and network connectivity.
# WHAT: Verify that this DC meets the three core prerequisites for MDI sensor installation
# WHY: The MDI sensor requires Server 2016+, at least 2 CPU cores + 6 GB RAM, and .NET 4.7+.
# Installing on a DC that doesn't meet these minimums causes sensor crashes or data loss.
# OUTPUT: OS version, CPU core count, RAM in GB, and .NET Framework version with pass/fail assessment
# Check OS version (must be Server 2016+ - build 10.0.14393 or higher)
[System.Environment]::OSVersion.Version
# Check available CPU cores and RAM
# MDI sensor minimum: 2 cores, 6 GB RAM. High-traffic DCs (>10K auth/sec) need more.
$cpu = (Get-CimInstance Win32_Processor | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
$ramGB = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 1)
Write-Host "CPU Cores: $cpu | RAM: ${ramGB} GB"
# Check .NET Framework version (must be 4.7+ for sensor compatibility)
# The Release value maps to specific .NET versions per Microsoft documentation
$netRelease = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" -EA SilentlyContinue).Release
$friendly = if($netRelease -ge 533320){'.NET 4.8.1+'} elseif($netRelease -ge 528040){'.NET 4.8'} elseif($netRelease -ge 461808){'.NET 4.7.2'} else {"Below 4.7 (Release: $netRelease)"}
Write-Host ".NET Framework: $friendly"# WHAT: Test outbound HTTPS (TCP 443) connectivity from this DC to MDI cloud service endpoints
# WHY: MDI sensors communicate with the cloud service over HTTPS. If these endpoints are blocked
# by a firewall or proxy, the sensor cannot send telemetry and alerts will not be generated.
# OUTPUT: OK/FAILED status for each endpoint. All must show OK before installing the sensor.
# - sensor.atp.azure.com: Primary MDI cloud service for telemetry and alert data
# - login.microsoftonline.com: Azure AD authentication for sensor registration
# - dc.services.visualstudio.com: Application Insights telemetry for sensor health
$testUrls = @(
"sensor.atp.azure.com",
"login.microsoftonline.com",
"dc.services.visualstudio.com"
)
foreach ($url in $testUrls) {
$result = Test-NetConnection -ComputerName $url -Port 443 -WarningAction SilentlyContinue
$status = if($result.TcpTestSucceeded) { "OK" } else { "FAILED" }
Write-Host "[$status] $url : Port 443" -ForegroundColor $(if($result.TcpTestSucceeded){'Green'}else{'Red'})
}# WHAT: Run prerequisite checks on ALL domain controllers in bulk via PowerShell remoting
# WHY: Manually checking each DC is time-consuming in large environments. This script validates
# CPU (โฅ2 cores), RAM (โฅ6 GB), and .NET Framework (โฅ4.7, release 461308) on every DC.
# OUTPUT: Table with DC name, CPU cores, RAM, .NET release, and a Ready (True/False) column.
# DCs showing Ready=False must be upgraded before sensor installation.
$dcs = (Get-ADDomainController -Filter *).Name
$results = foreach ($dc in $dcs) {
Invoke-Command -ComputerName $dc -ScriptBlock {
$cpu = (Get-CimInstance Win32_Processor | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
$ramGB = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 1)
# Release 461308 = .NET 4.7.1 (minimum supported by MDI sensor)
$netRelease = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" -EA SilentlyContinue).Release
[PSCustomObject]@{
DC = $env:COMPUTERNAME; CPUCores = $cpu; RamGB = $ramGB; DotNetRel = $netRelease
Ready = ($cpu -ge 2 -and $ramGB -ge 6 -and $netRelease -ge 461308)
}
} -ErrorAction SilentlyContinue
}
$results | Format-Table -AutoSizeMDI requires a directory service account for AD queries and entity resolution. A gMSA is recommended because it handles automatic password rotation.
# WHAT: Check for and create the KDS (Key Distribution Services) Root Key in Active Directory
# WHY: gMSA accounts use the KDS Root Key to generate and rotate passwords automatically.
# Without this key, New-ADServiceAccount will fail. The key must replicate to all DCs.
# OUTPUT: Existing key details (GUID, creation time) or confirmation of new key creation
# Check if KDS Root Key exists (required for gMSA)
Get-KdsRootKey
# If no key exists, create one
# Production: -EffectiveImmediately still requires ~10 hours for AD replication across all DCs
Add-KdsRootKey -EffectiveImmediately
# Lab only: backdate the effective time to skip the 10-hour replication wait
# WARNING: Do NOT use this in production - DCs that haven't replicated the key will reject gMSA requests
# Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10))# WHAT: Create a Group Managed Service Account (gMSA) for MDI sensor directory queries
# WHY: MDI sensors need an AD account to query objects, resolve entities, and read deleted items.
# A gMSA is preferred over a standard account because AD automatically rotates its password
# every 30 days - eliminating password management overhead and reducing credential theft risk.
# OUTPUT: gMSA created in AD, installed on the local DC, and validated (Test returns True/False)
# Create the gMSA - PrincipalsAllowedToRetrieveManagedPassword limits which computers can use it
# AES-only encryption prevents downgrade attacks (RC4/DES are vulnerable to Kerberoasting)
New-ADServiceAccount -Name "svc-MDI" `
-DNSHostName "svc-MDI.contoso.com" `
-Description "Service account for Microsoft Defender for Identity sensors" `
-PrincipalsAllowedToRetrieveManagedPassword "Domain Controllers" `
-KerberosEncryptionType AES128,AES256
# Install the gMSA on each DC (caches the password locally so the sensor can use it)
Install-ADServiceAccount -Identity "svc-MDI"
# Verify the gMSA can be used on this DC (should return True)
# If False: the DC is not in the PrincipalsAllowedToRetrieveManagedPassword group
Test-ADServiceAccount -Identity "svc-MDI"# WHAT: Grant the gMSA read access to the AD Deleted Objects container
# WHY: MDI needs to read deleted AD objects to detect account deletions used to cover tracks
# and to correlate historical entity data. The Deleted Objects container has restricted ACLs
# by default - only Domain Admins can read it. The gMSA needs explicit Generic Read (GR).
# OUTPUT: ACL updated on CN=Deleted Objects. No output on success; errors indicate permission issues.
# /takeOwnership: Required to modify ACLs on the system-protected container
# /G: Grant permission - GR = Generic Read, /I:T = Inherit to child objects (This object and children)
$deletedObjectsDN = "CN=Deleted Objects," + (Get-ADDomain).DistinguishedName
dsacls $deletedObjectsDN /takeOwnership
dsacls $deletedObjectsDN /G "contoso\svc-MDI$:GR" /I:TCreate your MDI instance in the Microsoft Defender portal and download the sensor installer.
Azure ATP Sensor Setup.zip\\contoso.com\NETLOGON\MDI-Sensor\# WHAT: Create a network share for the MDI sensor installer and extract it for deployment
# WHY: Placing the installer in NETLOGON ensures it's replicated to all DCs via DFS-R,
# making it accessible from any DC during automated bulk deployment (Step 7).
# OUTPUT: Extracted installer at \\DC01\NETLOGON\MDI-Sensor\Setup\Azure ATP Sensor Setup.exe
New-Item -Path "\\DC01\NETLOGON\MDI-Sensor" -ItemType Directory -Force
Copy-Item -Path "C:\Downloads\Azure ATP Sensor Setup.zip" -Destination "\\DC01\NETLOGON\MDI-Sensor\"
Expand-Archive -Path "\\DC01\NETLOGON\MDI-Sensor\Azure ATP Sensor Setup.zip" `
-DestinationPath "\\DC01\NETLOGON\MDI-Sensor\Setup" -ForceConfigure the gMSA in MDI settings so sensors know which account to use for AD queries.
contoso\svc-MDI$ (include the $ suffix)contoso.comInstall the sensor on your pilot DC first. Use the silent installation for scripted, reproducible deployments.
\\contoso.com\NETLOGON\MDI-Sensor\Setup\Azure ATP Sensor Setup.exe as Administrator# WHAT: Install the MDI sensor silently (unattended) on a domain controller
# WHY: Silent installation enables scripted, reproducible deployments across many DCs.
# The /quiet flag suppresses the GUI wizard. The access key authenticates the sensor
# to your MDI tenant in the Defender portal.
# OUTPUT: AATPSensor service running, and the last 20 lines of sensor.log confirming startup
# Replace YOUR-ACCESS-KEY with the tenant-specific key from Defender portal > Settings > Identities
$accessKey = "YOUR-ACCESS-KEY-HERE"
$installerPath = "\\contoso.com\NETLOGON\MDI-Sensor\Setup\Azure ATP Sensor Setup.exe"
# /quiet: No GUI | NetFrameworkCommandLineArguments: Silently installs .NET if needed
# AccessKey: Authenticates this sensor to your MDI cloud instance
Start-Process -FilePath $installerPath `
-ArgumentList "/quiet", "NetFrameworkCommandLineArguments=`"/q`"", "AccessKey=`"$accessKey`"" `
-Wait -NoNewWindow
# Verify the sensor service is running (Status should be "Running")
Get-Service -Name "AATPSensor" | Select-Object Name, Status, StartType
# Check the installation log for errors or successful startup confirmation
Get-Content "C:\Program Files\Azure Advanced Threat Protection Sensor\Logs\sensor.log" -Tail 20After validating the pilot, deploy sensors to all remaining DCs using PowerShell automation.
# WHAT: Automated bulk deployment of MDI sensor to all remaining domain controllers
# WHY: After validating the sensor on a pilot DC, this script deploys to every other DC
# using PowerShell remoting. Each DC copies the installer locally, runs it silently,
# and reports back whether the AATPSensor service started successfully.
# OUTPUT: Table with DC name, installer exit code, sensor status, and SUCCESS/CHECK/ERROR result
# Any DC showing CHECK or ERROR requires manual investigation.
$accessKey = "YOUR-ACCESS-KEY-HERE"
$installerShare = "\\contoso.com\NETLOGON\MDI-Sensor\Setup\Azure ATP Sensor Setup.exe"
$pilotDC = "DC01"
# Exclude the pilot DC (already installed in Step 6)
$targetDCs = (Get-ADDomainController -Filter *).Name | Where-Object { $_ -ne $pilotDC }
$results = foreach ($dc in $targetDCs) {
Write-Host "Deploying to $dc..." -ForegroundColor Cyan
try {
$r = Invoke-Command -ComputerName $dc -ScriptBlock {
param($installer, $key)
# Copy installer locally to avoid network latency during installation
$localPath = "C:\Temp\MDI-Setup"
New-Item -Path $localPath -ItemType Directory -Force | Out-Null
Copy-Item -Path $installer -Destination "$localPath\Setup.exe" -Force
# Run silent install with the tenant access key
$proc = Start-Process -FilePath "$localPath\Setup.exe" `
-ArgumentList "/quiet","NetFrameworkCommandLineArguments=`"/q`"","AccessKey=`"$key`"" `
-Wait -PassThru -NoNewWindow
# Verify the sensor service started after installation
$svc = Get-Service -Name "AATPSensor" -ErrorAction SilentlyContinue
[PSCustomObject]@{ ExitCode = $proc.ExitCode; Status = $svc.Status }
} -ArgumentList $installerShare, $accessKey -ErrorAction Stop
[PSCustomObject]@{ DC=$dc; ExitCode=$r.ExitCode; Sensor=$r.Status; Result=if($r.Status -eq 'Running'){'SUCCESS'}else{'CHECK'} }
} catch {
[PSCustomObject]@{ DC=$dc; ExitCode='N/A'; Sensor='ERROR'; Result=$_.Exception.Message }
}
}
$results | Format-Table -AutoSizeVerify all sensors are healthy, connected, and collecting data.
Restart-Service AATPSensor# WHAT: Check the MDI sensor and updater service status on every domain controller
# WHY: After deploying sensors, verify all DCs report healthy. The AATPSensor service captures
# traffic; the AATPSensorUpdater service handles automatic version updates from Microsoft.
# Any DC where the sensor is not Running represents a detection gap.
# OUTPUT: Per-DC status line: Sensor status, Updater status, and installed sensor version
# Look for: Sensor=Running, Updater=Running, Version matching latest release
$dcs = (Get-ADDomainController -Filter *).Name
foreach ($dc in $dcs) {
$svc = Invoke-Command -ComputerName $dc -ScriptBlock {
$sensor = Get-Service -Name "AATPSensor" -ErrorAction SilentlyContinue
$updater = Get-Service -Name "AATPSensorUpdater" -ErrorAction SilentlyContinue
[PSCustomObject]@{
Sensor=$sensor.Status; Updater=$updater.Status
Version=(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Azure Advanced Threat Protection\Sensor" -EA SilentlyContinue).InstallationVersion
}
} -EA SilentlyContinue
Write-Host "$dc | Sensor: $($svc.Sensor) | Updater: $($svc.Updater) | v$($svc.Version)"
}Verify MDI is discovering entities (users, computers, groups) and generating security assessments.
Set up monitoring procedures to maintain sensor health and respond to issues proactively.
# WHAT: Automated daily health monitoring script for all MDI sensors across the AD forest
# WHY: Sensors can stop due to resource exhaustion, service crashes, OS patches, or GPO changes.
# This script should run as a scheduled task (daily) to catch unhealthy sensors within 24h.
# OUTPUT: Green HEALTHY / Red UNHEALTHY / Yellow ERROR status per DC
# If any unhealthy sensors are found, a summary with required actions is displayed.
# Integrate the $unhealthy output with your alerting system (email, Teams, ITSM ticket).
$dcs = (Get-ADDomainController -Filter *).Name
$unhealthy = @()
foreach ($dc in $dcs) {
try {
$status = Invoke-Command -ComputerName $dc -ScriptBlock {
$svc = Get-Service -Name "AATPSensor" -EA SilentlyContinue
[PSCustomObject]@{ Name = $env:COMPUTERNAME; Status = if($svc){$svc.Status}else{"NOT INSTALLED"} }
} -ErrorAction Stop
if ($status.Status -ne "Running") {
$unhealthy += $status
Write-Host "UNHEALTHY: $dc. $($status.Status)" -ForegroundColor Red
} else {
Write-Host "HEALTHY: $dc" -ForegroundColor Green
}
} catch {
# DC is unreachable - could indicate network issue or DC is offline
$unhealthy += [PSCustomObject]@{ Name = $dc; Status = "UNREACHABLE" }
Write-Host "ERROR: $dc. Cannot connect" -ForegroundColor Yellow
}
}
if ($unhealthy.Count -gt 0) {
Write-Host "`nACTION REQUIRED: $($unhealthy.Count) unhealthy sensor(s) found" -ForegroundColor Red
$unhealthy | Format-Table -AutoSize
# TODO: Send alert to SOC via email/Teams webhook/ITSM ticket
}| Resource | Description |
|---|---|
| What is Microsoft Defender for Identity? | Product overview and capabilities |
| MDI prerequisites | System requirements and network configuration |
| Install the MDI sensor | Step-by-step sensor installation guide |
| Directory service accounts for MDI | Configure gMSA and standard service accounts |
| Capacity planning for MDI | Sizing guidance and performance considerations |
| MDI health alerts | Monitor and troubleshoot sensor health |
| Configure proxy settings | Proxy and firewall configuration for sensor connectivity |
| Identity security assessments | Posture recommendations and remediation |