Plan and deploy Microsoft Defender for Identity sensors on domain controllers, AD FS, AD CS, and Entra Connect servers. Covers sensor v2.x and v3.x selection, proxy configuration, gMSA setup, automated deployment, and ongoing health monitoring.
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 and other identity-role servers โ capturing authentication traffic (Kerberos, NTLM), LDAP queries, DNS lookups, and directory replication events in real time. No port mirroring required. Sensors are supported on domain controllers, AD FS federation servers, AD CS certification authority servers, and Microsoft Entra Connect servers. Two sensor versions exist: v3.x (preferred for Windows Server 2019+ DCs already running Defender for Endpoint โ portal-activated, uses local system account) and v2.x (classic installer for older DCs and all non-DC identity servers). This lab walks you through a complete enterprise deployment across all supported server types, including proxy configuration, gMSA setup, automated bulk deployment, and 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.
| Server Role | Sensor Version | Notes |
|---|---|---|
| Domain Controller (WS 2019+) | v3.x (recommended) or v2.x | v3.x requires MDE already deployed; uses local system account |
| Domain Controller (WS 2016) | v2.x only | Install via downloaded setup package |
| AD FS Federation Server | v2.x (if standalone); v3.x (if on DC WS2019+) | Not required on WAP servers |
| AD CS (Certification Authority) | v2.x (if standalone); v3.x (if on DC WS2019+) | CA Role Service only; skip offline servers |
| Microsoft Entra Connect | v2.x (if standalone); v3.x (if on DC WS2019+) | Deploy on both active and staging servers |
| RODC (Read-Only DC) | v2.x | Supported; deploy for full coverage |
<workspace>sensorapi.atp.azure.com*.atp.azure.com via DNSNew-MDIDSA, Test-MDISensorApiConnection, Set-MDIConfiguration, and more โ Install-Module DefenderForIdentity -Scope AllUsers. Supported on Windows PowerShell 5.1 and PowerShell 7.4+. On PS 7.4+, first run Import-Module -Name GroupPolicy -SkipEditionCheck. PowerShell 7.0–7.3 is not supported.Understand the MDI deployment model before installing sensors. Sensors run as services on supported servers, capturing authentication protocols, LDAP queries, DNS lookups, and directory replication traffic via Windows Event Tracing (ETW) and network parsing.
Use sensor v3.x if: the server is a domain controller running Windows Server 2019 or later AND Defender for Endpoint is already deployed. Activation is done from the Defender portal โ no installer download needed. Runs under the LocalSystem account โ no DSA or gMSA is used or configurable.
Use sensor v2.x if: the DC runs Windows Server 2016, OR the server is a standalone AD FS / AD CS / Entra Connect server. Uses the downloaded setup package. A DSA/gMSA is required for AD FS, AD CS, and Entra Connect sensors; for DC sensors it is optional but recommended โ without one, SAM-R lateral movement queries, domain and trust mapping, cross-domain LDAP, and Deleted Objects container access are all disabled.
Mixed environments are fully supported โ v2.x and v3.x sensors report to the same workspace.
# 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"GroupPolicy module with -SkipEditionCheck โ the code block below handles this automatically. PowerShell 7.0–7.3 is not supported.# WHAT: Install the DefenderForIdentity module and handle PS version compatibility
# WHY: The module depends on GroupPolicy. PS 5.1 loads it natively; PS 7.4+ needs
# -SkipEditionCheck. PS 7.0-7.3 is not supported - upgrade or use PS 5.1.
# Step 0: handle PS version compatibility
if ($PSVersionTable.PSEdition -eq 'Core') {
if ($PSVersionTable.PSVersion -lt [Version]'7.4') {
throw "PowerShell $($PSVersionTable.PSVersion) is not supported. Use PS 5.1 or PS 7.4+."
}
# PS 7.4+: import GroupPolicy with -SkipEditionCheck before loading the module
Import-Module -Name GroupPolicy -SkipEditionCheck
}
# Install once on any management server with PSGallery access
# -AllowClobber prevents conflicts with the older ATPModule cmdlets
Install-Module -Name DefenderForIdentity -Scope AllUsers -Force -AllowClobber
Import-Module DefenderForIdentity
Get-Command -Module DefenderForIdentity | Select-Object NameUse Test-MDISensorApiConnection from the module for a definitive connectivity test. Fall back to Test-NetConnection if the module is not yet installed.
# WHAT: Validate connectivity to your MDI workspace API endpoint using the module
# WHY: Test-MDISensorApiConnection tests the exact endpoint and auth path the sensor uses โ
# it is more accurate than a generic TCP test and catches proxy/cert issues too.
# Replace "contoso-corp" with your MDI workspace name from:
# security.microsoft.com > Settings > Identities > About
Import-Module DefenderForIdentity
$workspaceName = "contoso-corp"
$apiUrl = "https://$workspaceName`sensorapi.atp.azure.com"
# Test via module (requires module on this machine)
Test-MDISensorApiConnection -BypassConfiguration -SensorApiUrl $apiUrl -SensorType 'Classic'
# Fallback: manual TCP check if module is unavailable
$endpoints = @("$workspaceName`sensorapi.atp.azure.com","crl.microsoft.com","ctldl.windowsupdate.com","www.microsoft.com")
foreach ($ep in $endpoints) {
$r = Test-NetConnection -ComputerName $ep -Port 443 -WarningAction SilentlyContinue
$s = if ($r.TcpTestSucceeded) { "OK " } else { "FAILED" }
Write-Host "[$s] $ep" -ForegroundColor $(if($r.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.
# Requires: ActiveDirectory module (RSAT). Detects OS and offers install if missing.
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
$isClient = (Get-CimInstance Win32_OperatingSystem).ProductType -eq 1
Write-Warning "ActiveDirectory module (RSAT) not found."
if ($isClient) { Write-Host " Install: Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0" -ForegroundColor Cyan }
else { Write-Host " Install: Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools" -ForegroundColor Cyan }
$yn = Read-Host "Install RSAT now? [Y/N]"
if ($yn -match '^[Yy]') {
if ($isClient) { Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 }
else { Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools }
} else { throw "RSAT not installed. Re-run after installing." }
}
Import-Module ActiveDirectory
$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 -AutoSize# WHAT: Run Microsoft's official MDI prerequisite validation script
# WHY: Test-MdiReadiness.ps1 checks all environment requirements in one pass โ
# OS version, .NET, disk, network, proxy, ports, and sensor service accounts.
# Resolves issues it finds before starting sensor deployment.
# Option A: Run directly from Defender XDR portal download
# Download from: security.microsoft.com > Settings > Identities > Tools (Preview)
# Option B: Run from GitHub (verify the script before executing)
# https://github.com/microsoft/Microsoft-Defender-for-Identity
# After downloading, run on each target server (or remotely):
.\Test-MdiReadiness.ps1
# To test all DCs in the domain remotely:
# Requires: ActiveDirectory module (RSAT). Detects OS and offers install if missing.
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
$isClient = (Get-CimInstance Win32_OperatingSystem).ProductType -eq 1
Write-Warning "ActiveDirectory module (RSAT) not found."
if ($isClient) { Write-Host " Install: Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0" -ForegroundColor Cyan }
else { Write-Host " Install: Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools" -ForegroundColor Cyan }
$yn = Read-Host "Install RSAT now? [Y/N]"
if ($yn -match '^[Yy]') {
if ($isClient) { Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 }
else { Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools }
} else { throw "RSAT not installed. Re-run after installing." }
}
Import-Module ActiveDirectory
$dcs = (Get-ADDomainController -Filter *).Name
foreach ($dc in $dcs) {
Invoke-Command -ComputerName $dc -FilePath .\Test-MdiReadiness.ps1
}If your domain controllers or identity servers cannot reach the internet directly, configure a proxy. MDI does not support SSL inspection or intercepting proxies โ the proxy must pass traffic through unchanged. Skip this step if your servers have direct internet access.
# WHAT: Test outbound connectivity to MDI endpoints โ run on each sensor server
# WHY: If these fail, configure proxy settings below before installing the sensor.
# Replace "contoso-corp" with your actual MDI workspace name from:
# security.microsoft.com > Settings > Identities > About
$workspaceName = "contoso-corp"
$endpoints = @(
"$workspaceName`sensorapi.atp.azure.com",
"crl.microsoft.com", # Certificate revocation list
"ctldl.windowsupdate.com", # Trusted root update
"www.microsoft.com" # PKI certs (pki/* and pkiops/*)
)
foreach ($ep in $endpoints) {
$r = Test-NetConnection -ComputerName $ep -Port 443 -WarningAction SilentlyContinue
$s = if ($r.TcpTestSucceeded) { "OK " } else { "FAILED" }
Write-Host "[$s] $ep" -ForegroundColor $(if($r.TcpTestSucceeded){'Green'}else{'Red'})
}# WHAT: Install the MDI sensor and set proxy in a single command
# WHY: Configuring proxy at install time is the cleanest method โ only MDI sensor
# services use this proxy, not other system services.
# Replace values with your proxy address, domain account, and access key.
$installer = "\\contoso.com\NETLOGON\MDI-Sensor\Setup\Azure ATP Sensor Setup.exe"
$accessKey = "YOUR-ACCESS-KEY-HERE"
$proxyUrl = "http://proxy.contoso.com:8080"
$proxyUser = "CONTOSO\svc-proxy"
$proxyPass = "ProxyPassword"
Start-Process -FilePath $installer -Wait -NoNewWindow -ArgumentList `
"/quiet",
"NetFrameworkCommandLineArguments=`"/q`"",
"AccessKey=`"$accessKey`"",
"ProxyUrl=`"$proxyUrl`"",
"ProxyUserName=`"$proxyUser`"",
"ProxyUserPassword=`"$proxyPass`""# PS 5.1 or PS 7.4+ required. On PS 7.4+: Import-Module -Name GroupPolicy -SkipEditionCheck
# WHAT: View and update proxy settings using the DefenderForIdentity PowerShell module
# WHY: Use this to update proxy settings on already-deployed sensors without reinstalling.
# Install the module first if not present:
# Install-Module DefenderForIdentity -Force
# View current proxy config
Get-MDISensorProxyConfiguration
# Set proxy (unauthenticated)
Set-MDISensorProxyConfiguration -ProxyUrl 'http://proxy.contoso.com:8080'
# Set proxy with credentials (use Get-Credential for secure interactive input)
$cred = Get-Credential -Message "Enter proxy credentials (domain\user format)"
Set-MDISensorProxyConfiguration -ProxyUrl 'http://proxy.contoso.com:8080' -ProxyCredential $cred
# Remove proxy config entirely
Clear-MDISensorProxyConfiguration# WHAT: Set proxy using the sensor deployment tool (cmd / batch scripts)
# Path: C:\Program Files\Azure Advanced Threat Protection Sensor\<version>\
# Run from the sensor installation directory on each DC.
# Set proxy with credentials
Microsoft.Tri.Sensor.Deployment.Deployer.exe ProxyUrl="http://proxy.contoso.com:8080" ProxyUserName="CONTOSO\svc-proxy" ProxyUserPassword="myP@ssword"
# Remove proxy configuration
Microsoft.Tri.Sensor.Deployment.Deployer.exe ClearProxyConfiguration*.atp.azure.com. Certificate pinning will cause authentication failures.A Directory Service Account (DSA) applies to v2.x sensors only. A gMSA is the recommended DSA type because AD rotates its password automatically.
# 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
# Requires: ActiveDirectory module (RSAT). Detects OS and offers install if missing.
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
$isClient = (Get-CimInstance Win32_OperatingSystem).ProductType -eq 1
Write-Warning "ActiveDirectory module (RSAT) not found."
if ($isClient) { Write-Host " Install: Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0" -ForegroundColor Cyan }
else { Write-Host " Install: Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools" -ForegroundColor Cyan }
$yn = Read-Host "Install RSAT now? [Y/N]"
if ($yn -match '^[Yy]') {
if ($isClient) { Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 }
else { Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools }
} else { throw "RSAT not installed. Re-run after installing." }
}
Import-Module ActiveDirectory
# 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)
Import-Module ActiveDirectory
# 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"The DefenderForIdentity module wraps the manual gMSA steps into a single command and automatically validates permissions, KDS key, and SAMR access.
# PS 5.1 or PS 7.4+ required. On PS 7.4+: Import-Module -Name GroupPolicy -SkipEditionCheck
# WHAT: Create the MDI Directory Service Account (gMSA) using the module and validate it
# WHY: New-MDIDSA handles KDS key check, New-ADServiceAccount, Install-ADServiceAccount,
# and the Deleted Objects ACL grant in one call - reducing manual error.
# Test-MDIDSA then verifies the account has all permissions MDI sensors actually need.
Import-Module DefenderForIdentity
# Create the gMSA - -GmsaGroupName limits which computers can retrieve the password
New-MDIDSA -Identity "svc-MDI" -GmsaGroupName "Domain Controllers"
# Validate all required permissions are in place (returns True on success)
$dsaOk = Test-MDIDSA -Identity "svc-MDI"
if (-not $dsaOk) { Write-Warning "DSA validation failed - check AD permissions and KDS key replication" }
# For multi-domain forests, repeat for each domain:
# New-MDIDSA -Identity "svc-MDI" -GmsaGroupName "Domain Controllers" -Domain "child.contoso.com"New-MDIDSA automatically sets the Deleted Objects container permissions โ skip the code below if you used the module approach above.# WHAT: Grant read access to the AD Deleted Objects container for the MDI service account
# WHY: MDI needs to read deleted AD objects to correlate historical entity data.
# The Deleted Objects container has restricted ACLs by default.
# PERMISSIONS: List Contents (LC) + Read Property (RP) โ grant to a security GROUP, not directly
# to the gMSA. The gMSA must be a member of this group.
# NOTE: New-MDIDSA (module approach above) handles this automatically.
Import-Module ActiveDirectory
# Step 1: For gMSA, create a security group and add the gMSA as a member
$groupName = "MDI-DSA-Readers"
New-ADGroup -Name $groupName -SamAccountName $groupName -GroupCategory Security `
-GroupScope Universal -Description "Group for MDI DSA Deleted Objects read access"
Add-ADGroupMember -Identity $groupName -Members "svc-MDI$"
# Step 2: Get the Deleted Objects container DN and domain name
$distinguishedName = ([adsi]'').distinguishedName.Value
$deletedObjectsDN = "CN=Deleted Objects,$distinguishedName"
$domain = ([adsi]'').name.Value
# Step 3: Take ownership, then grant LC+RP (LCRP) to the group
C:\Windows\System32\dsacls.exe "$deletedObjectsDN" /takeOwnership
C:\Windows\System32\dsacls.exe "$deletedObjectsDN" /G "${domain}\${groupName}:LCRP"
# For a regular (non-gMSA) user account, grant permissions directly to the account:
# C:\Windows\System32\dsacls.exe "$deletedObjectsDN" /G "contoso\mdiSvcUser:LCRP"Create your MDI instance in the Microsoft Defender portal and download the sensor installer.
Azure ATP Sensor Setup.zip (the installer retains the legacy "Azure ATP" filename)\\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" -ForceRegister the v2.x DSA/gMSA in MDI portal settings so sensors know which account to use. Skip this step if all sensors are v3.x โ v3.x uses LocalSystem and has no DSA configuration.
svc-MDI$ (account name only, with $ suffix โ no domain prefix)contoso.com (full FQDN of the domain where the account is located)Install 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, StartTypeAfter 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)
# Requires: ActiveDirectory module (RSAT). Detects OS and offers install if missing.
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
$isClient = (Get-CimInstance Win32_OperatingSystem).ProductType -eq 1
Write-Warning "ActiveDirectory module (RSAT) not found."
if ($isClient) { Write-Host " Install: Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0" -ForegroundColor Cyan }
else { Write-Host " Install: Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools" -ForegroundColor Cyan }
$yn = Read-Host "Install RSAT now? [Y/N]"
if ($yn -match '^[Yy]') {
if ($isClient) { Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 }
else { Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools }
} else { throw "RSAT not installed. Re-run after installing." }
}
Import-Module ActiveDirectory
$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 -AutoSizeDeploy the v2.x sensor on standalone AD FS, AD CS, and Entra Connect servers (those that are not domain controllers). If these roles run on a domain controller running Windows Server 2019 or later, use the v3.x activation in Step 7 instead.
# WHAT: Silent install of MDI v2.x sensor on AD FS, AD CS, or Entra Connect servers
# WHY: Same installer as DCs, but these servers require a Directory Service Account
# and additional database permission grants below.
$accessKey = "YOUR-ACCESS-KEY-HERE"
$installer = "\\contoso.com\NETLOGON\MDI-Sensor\Setup\Azure ATP Sensor Setup.exe"
# List of non-DC identity servers to deploy to
$identityServers = @("ADFS01","ADFS02","ADCS01","EntraConnect01","EntraConnect02-Staging")
$results = foreach ($server in $identityServers) {
Write-Host "Deploying to $server..." -ForegroundColor Cyan
try {
$r = Invoke-Command -ComputerName $server -ScriptBlock {
param($installer, $key)
$localPath = "C:\Temp\MDI-Setup"
New-Item -Path $localPath -ItemType Directory -Force | Out-Null
Copy-Item -Path $installer -Destination "$localPath\Setup.exe" -Force
$proc = Start-Process -FilePath "$localPath\Setup.exe" `
-ArgumentList "/quiet","NetFrameworkCommandLineArguments=`"/q`"","AccessKey=`"$key`"" `
-Wait -PassThru -NoNewWindow
$svc = Get-Service -Name "AATPSensor" -ErrorAction SilentlyContinue
[PSCustomObject]@{ ExitCode = $proc.ExitCode; Status = $svc.Status }
} -ArgumentList $installer, $accessKey -ErrorAction Stop
[PSCustomObject]@{ Server=$server; ExitCode=$r.ExitCode; Status=$r.Status; Result=if($r.Status -eq 'Running'){'SUCCESS'}else{'CHECK'} }
} catch {
[PSCustomObject]@{ Server=$server; ExitCode='N/A'; Status='ERROR'; Result=$_.Exception.Message }
}
}
$results | Format-Table -AutoSizeMDI needs read access to the AD FS configuration database to monitor federation authentication. Run this on each AD FS server individually (permissions are not replicated).
# WHAT: Grant the MDI gMSA db_datareader access to the AD FS configuration database
# WHY: Without this, the sensor cannot read AD FS events and federation auth is invisible to MDI.
# Works for both Windows Internal Database (WID) and external SQL Server.
# Replace DOMAIN1\mdiSvc01 with your gMSA name (include the $ for gMSA: DOMAIN1\mdiSvc01$).
$gMSA = "DOMAIN1\mdiSvc01$"
$dbName = "AdfsConfigurationV4" # Check your AD FS version โ may be AdfsConfiguration or AdfsConfigurationV3
# For Windows Internal Database (WID):
$connString = "server=\\.\pipe\MICROSOFT##WID\tsql\query;database=$dbName;trusted_connection=true;"
# For external SQL Server, use: "server=SQLSERVER\INSTANCE;database=$dbName;trusted_connection=true;"
$conn = New-Object System.Data.SqlClient.SqlConnection($connString)
$conn.Open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = @"
USE [master];
CREATE LOGIN [$gMSA] FROM WINDOWS WITH DEFAULT_DATABASE=[master];
USE [$dbName];
CREATE USER [$gMSA] FOR LOGIN [$gMSA];
ALTER ROLE [db_datareader] ADD MEMBER [$gMSA];
GRANT CONNECT TO [$gMSA];
GRANT SELECT TO [$gMSA];
"@
$cmd.ExecuteNonQuery() | Out-Null
$conn.Close()
Write-Host "AD FS DB permissions granted for $gMSA on $dbName" -ForegroundColor Green# WHAT: Grant MDI sensor permissions to the Entra Connect ADSync database
# NOTE: Only required when ADSync DB is on an external SQL Server instance.
# Entra Connect uses LocalDB by default - check SQLInstance registry value first.
# Run on each Entra Connect server (both active and staging).
$entraConnectServerDomain = $env:USERDOMAIN
$entraConnectServerComputer = $env:COMPUTERNAME
$dbName = (Get-ItemProperty 'registry::HKLM\SYSTEM\CurrentControlSet\Services\ADSync\Parameters' -Name 'DBName').DBName
$sqlServer = (Get-ItemProperty 'registry::HKLM\SYSTEM\CurrentControlSet\Services\ADSync\Parameters' -Name 'Server').Server
$sqlInstance = (Get-ItemProperty 'registry::HKLM\SYSTEM\CurrentControlSet\Services\ADSync\Parameters' -Name 'SQLInstance').SQLInstance
$connString = "server={0}\{1};database={2};trusted_connection=true;" -f $sqlServer, $sqlInstance, $dbName
$conn = New-Object System.Data.SqlClient.SqlConnection($connString)
$conn.Open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = @"
USE [master];
CREATE LOGIN [{0}\{1}$] FROM WINDOWS WITH DEFAULT_DATABASE=[master];
USE [{2}];
CREATE USER [{0}\{1}$] FOR LOGIN [{0}\{1}$];
GRANT CONNECT TO [{0}\{1}$];
GRANT SELECT TO [{0}\{1}$];
GRANT EXECUTE ON OBJECT::{2}.dbo.mms_get_globalsettings TO [{0}\{1}$];
GRANT EXECUTE ON OBJECT::{2}.dbo.mms_get_connectors TO [{0}\{1}$];
"@ -f $entraConnectServerDomain, $entraConnectServerComputer, $dbName
$cmd.ExecuteNonQuery() | Out-Null
$conn.Close()
Write-Host "Entra Connect DB permissions granted" -ForegroundColor GreenDuring installation on an AD FS, AD CS, or Entra Connect server, the closest domain controller is automatically selected. Use the following steps to check or update it if needed.
DC01.contoso.com)# Validate AD FS sensor โ run in Defender portal > Hunting > Advanced Hunting
IdentityLogonEvents | where Protocol contains 'Adfs'
# Expected: rows with LogonType = "Logon with ADFS authentication"
# Validate AD CS sensor
IdentityDirectoryEvents | where Protocol == "Adcs"
# Expected: certificate issuance events (success and failure)Verify 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
# Requires: ActiveDirectory module (RSAT). Detects OS and offers install if missing.
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
$isClient = (Get-CimInstance Win32_OperatingSystem).ProductType -eq 1
Write-Warning "ActiveDirectory module (RSAT) not found."
if ($isClient) { Write-Host " Install: Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0" -ForegroundColor Cyan }
else { Write-Host " Install: Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools" -ForegroundColor Cyan }
$yn = Read-Host "Install RSAT now? [Y/N]"
if ($yn -match '^[Yy]') {
if ($isClient) { Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 }
else { Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools }
} else { throw "RSAT not installed. Re-run after installing." }
}
Import-Module ActiveDirectory
$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 }
} -EA SilentlyContinue
Write-Host "$dc | Sensor: $($svc.Sensor) | Updater: $($svc.Updater)"
}# PS 5.1 or PS 7.4+ required. On PS 7.4+: Import-Module -Name GroupPolicy -SkipEditionCheck
# WHAT: Run a full MDI configuration audit using the DefenderForIdentity module
# WHY: Test-MDIConfiguration checks that all required GPOs, audit policies, and configurations
# are correctly applied. New-MDIConfigurationReport generates an HTML+JSON report for
# review and record-keeping. Run these after every deployment.
Import-Module DefenderForIdentity
# Test all MDI-required domain configurations (audit policies, SAMR, etc.)
$configOk = Test-MDIConfiguration -Mode Domain -Configuration All
Write-Host "Domain config test: $(if($configOk){'PASSED'}else{'FAILED - review report'})" `
-ForegroundColor $(if($configOk){'Green'}else{'Red'})
# Also validate DSA permissions are still intact
$dsaOk = Test-MDIDSA -Identity "svc-MDI"
Write-Host "DSA permissions: $(if($dsaOk){'PASSED'}else{'FAILED - check gMSA'})" `
-ForegroundColor $(if($dsaOk){'Green'}else{'Red'})
# Generate a full configuration report (HTML + JSON)
$reportPath = "$env:USERPROFILE\Desktop\MDI-ConfigReport-$(Get-Date -Format 'yyyyMMdd-HHmm')"
New-MDIConfigurationReport -Path $reportPath
Write-Host "Report saved: $reportPath.html" -ForegroundColor CyanNew-MDIConfigurationReport output after each deployment. It serves as a change baseline โ run it again after any GPO or AD change to quickly spot configuration drift.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).
# Requires: ActiveDirectory module (RSAT). Detects OS and offers install if missing.
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
$isClient = (Get-CimInstance Win32_OperatingSystem).ProductType -eq 1
Write-Warning "ActiveDirectory module (RSAT) not found."
if ($isClient) { Write-Host " Install: Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0" -ForegroundColor Cyan }
else { Write-Host " Install: Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools" -ForegroundColor Cyan }
$yn = Read-Host "Install RSAT now? [Y/N]"
if ($yn -match '^[Yy]') {
if ($isClient) { Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 }
else { Install-WindowsFeature -Name RSAT-AD-PowerShell -IncludeManagementTools }
} else { throw "RSAT not installed. Re-run after installing." }
}
Import-Module ActiveDirectory
$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
}# PS 5.1 or PS 7.4+ required. On PS 7.4+: Import-Module -Name GroupPolicy -SkipEditionCheck
# WHAT: Generate a monthly MDI configuration audit report using the module
# WHY: Configurations drift over time as GPOs change and AD is modified. A monthly report
# provides a record of audit policy compliance and flags any missing configurations.
# Schedule this as a monthly scheduled task on a management server.
Import-Module DefenderForIdentity
$reportPath = "$env:USERPROFILE\Documents\MDI-Reports\MDI-Config-$(Get-Date -Format 'yyyy-MM')"
New-Item -Path (Split-Path $reportPath) -ItemType Directory -Force | Out-Null
New-MDIConfigurationReport -Path $reportPath
Write-Host "Monthly report: $reportPath.html" -ForegroundColor Cyan| 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 |
| Deploy MDI sensor v3.x | Activate v3.x sensor on Windows Server 2019+ domain controllers |
| MDI sensor v2.x prerequisites | Requirements for classic sensor on older DCs and non-DC servers |
| Configure sensors for AD FS, AD CS, Entra Connect | Non-DC identity server deployment and database permissions |
| Configure proxy settings | Proxy and firewall configuration for sensor connectivity |
| Identity security assessments | Posture recommendations and remediation |
| DefenderForIdentity PowerShell module | New-MDIDSA, Test-MDISensorApiConnection, Set-MDIConfiguration, and more |