Blog Post

Core Infrastructure and Security Blog
9 MIN READ

Identify Defender for Endpoint Onboarding Method using PowerShell - Part 2

edgarus71's avatar
edgarus71
Icon for Microsoft rankMicrosoft
Aug 19, 2025

To provide a layer of automation to discover what onboarding methods are used for a subset of devices in Defender for Endpoint, the following PowerShell script can be used to run on a set of devices you are troubleshooting and determine what method was used. It can be a useful alternative for administrators who do not have access to one or any of the MDE onboarding platforms.

In its simplest form, what the script does is, interrogate a list of computers in a .txt file, run a detection logic to find indicators that points to what method was used to do the onboarding e.g., Intune, LocalScript, GPO or MCM and present the results in a .csv file. 

 

As described in Part 1 of this article, onboarding devices in Defender for Endpoint can be done via different methods like Intune, MCM, GPO or LocalScript. Each method has its own requirements, intricacies, and purpose. Nonetheless, when doing troubleshooting on a subset of devices, it becomes an important piece of information to have to help determine what onboarding method was used for a quicker analysis. This script's purpose is to complement the enriched information provided by the MDE Client analyzer tool and offer an additional piece of information to aid the troubleshooting process. The script may be used on a large scale, but its performance may not be optimal. It is recommended to use it only on a subset of devices that may be experiencing issues related to Defender for Endpoint onboarding process. 

NOTE: Although not covered in detail in this article, If the script is used on persistent VDI devices that were onboarded, for example via GPO; The onboarding method will be highlighted as GPO. Outcome will be equivalent if onboarding is done by MCM or LocalScript. 

For non-persistent VDIs the script will show that those endpoints were onboarded using a LocalScript which in essence is what the option shown from the Defender portal stands for. The script used for non-persistent VDIs is an optimized version of the regular LocalScript, as an effort to minimize the onboarding processing time on these devices. Fig 1 reference. 

 

                                                                          Figure 1. onboarding script for non-persistent VDI devices. 

  High level execution of the script: 

Scans computers to determine onboarding method in MDE: 

This is done via PowerShell remoting: Ensure remote computers have enabled PS remoting. 

Doing so at scale via GPO / policy is recommended. 

This command can enable remoting on a given computer: Enable-PSRemoting -Force 

Improvements since inception: 

- Computer names were being truncated if they were longer than 15 characters. 

- Run the script with local admin rights on the machine from where the script is executed and if running remotely on multiple machines using a .txt file 

- The account used on the remote devices needs to have local admin rights too. 

 For further troubleshooting script execution refer to this link: PowerShell Remote troubleshooting

 Parameters used in the script: 

The script contains different parameters to evaluate with a great level of certainty if a device was onboarded using one of the known methods: 

 

  • ComputerList 

    Path to a text-file, containing all the computers to scan, one computer per line. 

    - Leading and trailing whitespaces will be trimmed 

    - Lines starting with a "#" symbol will be skipped 

  •  OutPath 

    Path where to export the results to, should point at a CSV file. 

  •  ComputerName 

    List of computers to process. In case you do not wish to externally prepare a list of computer names in a file. 

  •  Credential 

    Credentials to use for remote access. By default, the current user's credentials will be used. 

  •  LogPath 

    All scanned machines write a LOCAL logfile with the scan results. 

    This allows tracking local changes over time. 

    Defaults to: C:\temp\MDE-Onboarding-Detection.log 

    Set to "" (empty string) to disable the log. 

  •  HOW TO RUN - EXAMPLE

    PS C:\> .\MDEOnboardingScan.ps1 -ComputerName “computername” (single computer) 

    PS C:\> .\MDEOnboardingScan.ps1 )Script will prompt for missing parts - what files with computer names to read, where to store the results. Then it will scan the computers and write the results to a .csv file) 

 

Now, interrogating each of the variables I described in Part 1 of this article, the script goes hunting for the clues defined below, on the devices specified in the computer list text file. 

 OnboardingKey  = "HKLM:\SOFTWARE\Microsoft\Windows Advanced Threat Protection\Status" 

LocalScriptKey = "HKLM:\SOFTWARE\Policies\Microsoft\Windows Advanced Threat Protection" 

IntuneKey      = "HKLM:\SOFTWARE\Microsoft\Windows Defender" 

MCMLogPath    = "C:\Windows\CCM\Logs\EndpointProtectionAgent.log" 

 

The outcome of this process can be one of the ones described below: 

 

  • LocalScript: If only the latency regkey with a value of “demo” was detected the device was onboarded using the LocalScript option offered from Defender portal. 
  • Intune: If the regkey called MdmSubscriberIds is present with a value and no regkey “latency” key is present, the device was onboarded using Intune. 
  • LocalScript + IntuneManaged: Not an onboarding method per-se but wanted to highlight that whenever the “latency” regkey with the value of “demo” is present the onboarding happened using the LocalScript. If the regkey “latency” is present with a value of “demo” and the MdmSubscriberIds regkey is also present, it means the device was onboarded via LocalScript and Intune is managing the device. In this scenario, the regkey “latency” wins because it is a value that get sets only when onboarding is done via the LocalScript. 
  • MCM: The presence of the following log file “EndpointProtectionAgent.log” in the following path: “C:\Windows\CCM\Logs\” indicates device was onboarded using MCM. 
  • GPO: If the onboarding State regkey has a value of 1 and no latency regkey is present, nor MdMSubscriberId regkey exists, it is determined that the device was onboarded using a GPO. 

 PowerShell scripting code: 

Code disclaimer: 

The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.

NOTE: When using your scripting editing tool of choice, be aware of any additional spaces added by the copy/past operation. If you have issues copy/pasting the script in your script editing tool, I am attaching a copy of the script in zip format. Bottom of the article.

<# .SYNOPSIS Scans computers to determine how the devices were onboarded in Defender for Endpoint (MDE). 

.DESCRIPTION Scans computers to determine onboarding method in MDE. 

This is done via PowerShell remoting: Ensure remote computers have enabled remoting. 

Doing so at scale via GPO / policy is recommended. 

This command can enable remoting on a given computer: 

Improvements: computer names were being truncated if they were longer than 15 characters. 

Run the script with local admin rights on the machine from where the script is executed and if running remotely on multiple machines using a .txt file 

the account used on the remote devices needs to have local admin rights too. 

For further troubleshooting script execution refer to this link: 

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_troubleshooting?view=powershell-7.5 

 

Enable-PSRemoting -Force 

.PARAMETER ComputerList Path to a text-file, containing all the computers to scan, one computer per line. - Leading and trailing whitespaces will be trimmed - Lines starting with a "#" symbol will be skipped 

.PARAMETER OutPath Path where to export the results to. Should point at a CSV file. 

.PARAMETER ComputerName List of computers to process. In case you do not wish to externally prepare a list of computernames in a file. 

.PARAMETER Credential Credentials to use for remote access. By default, the current user's credentials will be used. 

.PARAMETER LogPath All scanned machines write a LOCAL logfile with the scan results. This allows tracking local changes over time. Defaults to: C:\temp\MDE-Onboarding-Detection.log Set to "" (empty string) to disable the log. 

.EXAMPLE PS C:> .\defenderOnboardingScan.ps1 

Script will prompt for missing parts - what files with computernames to read, where to store the results. 

Then will scan the computers and write the results. 

.NOTES Author: Edgar Parra, Friedrich Weinmann Company: Microsoft Version: 3.1.1 #> 

[CmdletBinding()] param ( [string] $ComputerList, [string] $OutPath, [string[]] $ComputerName, [PSCredential] $Credential, [AllowEmptyString()] [string] $LogPath = 'C:\temp\MDE-Onboarding-Detection.log' ) 

$ErrorActionPreference = 'Stop' trap { Write-Warning "Script failed: $" throw $ } 

function Show-OpenFileDialog { [OutputType([string])] [CmdletBinding()] param ( [string] $InitialDirectory = '.', [string] $Filter, [string] $Title, [switch] $MultiSelect ) 

Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue 

$dialog = [System.Windows.Forms.OpenFileDialog]::new() 

$dialog.InitialDirectory = Resolve-Path -Path $InitialDirectory 

$dialog.MultiSelect = $MultiSelect.ToBool() 

$dialog.Title = $Title 

if ($Filter) { $dialog.Filter = $Filter } 

$null = $dialog.ShowDialog() 

$dialog.FileNames 

} 

function Show-SaveFileDialog { [CmdletBinding()] param ( [string] $InitialDirectory = '.', [string] $Filter = '.', $Filename ) 

Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue 

$saveFileDialog = [Windows.Forms.SaveFileDialog]::new() 

$saveFileDialog.FileName = $Filename 

$saveFileDialog.InitialDirectory = Resolve-Path -Path $InitialDirectory 

$saveFileDialog.Title = "Save File to Disk" 

$saveFileDialog.Filter = $Filter 

$saveFileDialog.ShowHelp = $True 

 

$result = $saveFileDialog.ShowDialog() 

if ($result -eq "OK") { 

    $saveFileDialog.FileName 

} 

} 

function Resolve-ComputerTarget { [CmdletBinding()] param ( [AllowNull()] [AllowEmptyCollection()] [string[]] $ComputerName, 

    [AllowEmptyString()] 

    [string] $Path 

) 

 

if ($ComputerName) { 

    return $ComputerName | Where-Object { $_ -and $_ -notlike "#*" } | ForEach-Object { $_.Trim() } 

} 

if ($Path) { 

    return Get-Content -Path $Path | Where-Object { $_ -and $_ -notlike "#*" } | ForEach-Object { $_.Trim() } 

} 

 

$filesSelected = Show-OpenFileDialog -Filter 'Text files (*.txt)|*.txt|All files (*.*)|*.*' -MultiSelect -Title "Select Text-File with list of Computer Names!" 

if (-not $filesSelected) { 

    throw "No file with computers to scan selected!" 

} 

$computers = foreach ($filePath in $filesSelected) { 

    Get-Content -Path $filePath 

} 

$computers | Where-Object { $_ -and $_ -notlike "#*" } | ForEach-Object { $_.Trim() } 

} 

function Resolve-ExportFile { [CmdletBinding()] param ( [AllowEmptyString()] [string] $Path ) 

if ($Path) { 

    $parent = Split-Path -Path $Path 

    if (-not (Test-Path -Path $parent)) { 

        $null = New-Item -Path $parent -ItemType Directory -Force 

    } 

    return $Path 

} 

 

$result = Show-SaveFileDialog -Filename "mdeOnboarding-$(Get-Date -Format 'yyyy-MM-dd_HH-mm').csv" -Filter "CSV Files (*.csv)|*.csv" 

if (-not $result) { throw "No export path selected, cannot write report!" } 

$result 

} 

function Get-MdeOnboardingInfo { [CmdletBinding()] param ( [AllowEmptyCollection()] [string[]] $ComputerName, 

    [AllowNull()] 

    [PSCredential] $Credential, 

 

    [AllowEmptyString()] 

    [string] $LogPath 

) 

 

$code = { 

    param ( 

        [string] $TargetName, 

        [string] $LogPath 

    ) 

 

    function Write-Log { 

        param ( 

            [string] $Message, 

            [AllowEmptyString()] 

            [string] $Path 

        ) 

        if (-not $Path) { return } 

        $parent = Split-Path -Path $Path 

        if (-not (Test-Path -Path $parent)) { 

            $null = New-Item -Path $parent -ItemType Directory -Force 

        } 

        '{0:yyyy-MM-dd HH:mm:ss} {1}' -f (Get-Date).ToUniversalTime(), $Message | Add-Content -Path $Path 

    } 

 

    $paths = @{ 

        OnboardingKey  = "HKLM:\SOFTWARE\Microsoft\Windows Advanced Threat Protection\Status" 

        LocalScriptKey = "HKLM:\SOFTWARE\Policies\Microsoft\Windows Advanced Threat Protection" 

        IntuneKey      = "HKLM:\SOFTWARE\Microsoft\Windows Defender" 

        SccmLogPath    = "C:\Windows\CCM\Logs\EndpointProtectionAgent.log" 

    } 

 

    $status = @{ 

        LatencyDetected = $false 

        MdmDetected     = $false 

        SccmDetected    = $false 

        GpoDetected     = $false 

        Onboarded       = $false 

    } 

 

    $result = [PSCustomObject]@{ 

        ComputerName = $TargetName 

        Status       = "" 

        Success      = $true 

        Error        = $null 

    } 

 

    $onboardingData = Get-ItemProperty -Path $paths.OnboardingKey -ErrorAction Ignore 

    $localScriptData = Get-ItemProperty -Path $paths.LocalScriptKey -ErrorAction Ignore 

    $intuneData = Get-ItemProperty -Path $paths.IntuneKey -ErrorAction Ignore 

 

    if (-not $onboardingData -and -not $localScriptData) { 

        Write-Log 'Device is not onboarded.' $LogPath 

        $result.Status = 'NotOnboarded' 

        return $result 

    } 

 

    if ($onboardingData.OnboardingState -eq 1) { 

        $status.Onboarded = $true 

        Write-Log 'Device is onboarded' $LogPath 

    } 

 

    if ($localScriptData -and $localScriptData.latency -eq 'demo') { 

        $status.LatencyDetected = $true 

        Write-Log "Detected 'latency' = 'demo'." $LogPath 

    } 

 

    if (@($intuneData.MdmSubscriberIds | Where-Object { $_ }).Count -ge 1) { 

        $status.MdmDetected = $true 

        Write-Log "Detected non-empty 'MdmSubscriberIds'." $LogPath 

    } 

 

    if (Test-Path $paths.SccmLogPath) { 

        $status.SccmDetected = $true 

        Write-Log "Detected SCCM log file." $LogPath 

    } 

 

    if ($status.LatencyDetected -and -not $status.MdmDetected) { 

        $result.Status = "Local Script" 

    } 

    elseif (-not $status.LatencyDetected -and $status.MdmDetected) { 

        $result.Status = "Intune" 

    } 

    elseif ($status.LatencyDetected -and $status.MdmDetected) { 

        $result.Status = "Local Script + Intune managed" 

    } 

    elseif ($status.SccmDetected) { 

        $result.Status = "MCM" 

    } 

    elseif ($status.Onboarded) { 

        $result.Status = "GPO" 

        Write-Log "Device was onboarded via GPO" $LogPath 

    } 

    else { 

        $result.Status = "NotOnboarded" 

    } 

 

    return $result 

} 

 

if (-not $ComputerName) { throw "No computer names provided." } 

 

$results = @() 

 

foreach ($name in $ComputerName) { 

    $param = @{ 

        ComputerName = $name 

        ScriptBlock  = $code 

        ArgumentList = @($name, $LogPath) 

    } 

    if ($Credential) { $param.Credential = $Credential } 

 

    try { 

        $output = Invoke-Command param​ 

        $results += $output 

    } catch { 

        $results += [PSCustomObject]@{ 

            ComputerName = $name 

            Status       = "" 

            Success      = $false 

            Error        = $_.Exception.Message 

        } 

    } 

} 

 

return $results 

} 

$computers = Resolve-ComputerTarget -ComputerName $ComputerName -Path $ComputerList $outFile = Resolve-ExportFile -Path $OutPath Get-MdeOnboardingInfo -ComputerName $computers -Credential $Credential -LogPath $LogPath | Select-Object ComputerName, Status, Success, Error | Export-Csv -Path $outFile -NoTypeInformation 

Ensuring Effective Onboarding and with Microsoft Defender for Endpoint

Understanding the specific onboarding method used for Microsoft Defender for Endpoint (MDE) on your devices is crucial for efficient troubleshooting and ongoing management. Identifying the onboarding path—whether through LocalScript, Intune, MCM, or GPO—not only allows administrators to pinpoint configuration issues more rapidly but also helps in verifying compliance with organizational security policies. By quickly determining the onboarding method, administrators can tailor their remediation steps, ensure proper policy application, and minimize endpoint security gaps. This knowledge streamlines support efforts, reduces downtime, and fosters a more secure and resilient environment for users and data alike. 

 Explore additional resources:

For further insights and guidance on MDE onboarding and troubleshooting, consider reviewing these related articles: 

Updated Aug 19, 2025
Version 1.0

1 Comment

  • PJR_CDF's avatar
    PJR_CDF
    Iron Contributor

    Really appreciate the effort with this script, but this capability should be natively built into MDE with the onboarding method clearly visible in the device page for each device in the portal. Fingers crossed that's on the roadmap.