Act
Collector
A PowerShell module that an Intune remediation installs and runs on each device as SYSTEM. It changes no setting, signs every report with the device’s own Entra device key, and sends it to your ingest endpoint. There is no key, secret or token to deploy.
- Runs
- As SYSTEM, from an Intune remediation, daily
- Sends
- An inventory when the device changes (and at least every 7 days), then a small telemetry report every run
- Budget
- A minute and 5 MB per device as the target; the probes alone can take up to 100 seconds. Reports are a few KB compressed
- Changes on the device
- No setting. The remediation installs the module and writes a log; the collector keeps its state and two registry values
01
Before you start
| Requirement | How to tell |
|---|---|
| Microsoft Entra joined or Microsoft Entra hybrid joined | dsregcmd /status shows AzureAdJoined : YES.3 Entra registered (BYOD) and domain-only devices cannot report. |
| Enabled in Microsoft Entra | DeviceAuthStatus : SUCCESS; the field exists from Windows 10 21H1.3 A disabled or deleted device is refused. |
| Enrolled in Intune | The service drops reports from devices it cannot find in Intune. Autopilot devices meet this and the previous two. |
| Windows 10 2004 or Windows 11 | On older builds it reports what exists and marks the rest as not collected, rather than inventing it. |
| A licence that includes Remediations | Windows Enterprise E3 or E5, Windows Education A3 or A5, or Windows Virtual Desktop Access per user, for the devices’ users, on Windows Enterprise, Professional or Education.1 The tenant confirms it owns them once, under Tenant administration, Connectors and tokens, Windows data.8 |
| Your ingest endpoint | The https://…/v1/ingest address given when DoctorDO was deployed for your tenant. |
Why there is no certificate to deploy
When a device joins, the Entra Device Registration Service issues it an MS-Organization-Access certificate. On joined and hybrid joined devices it sits in the local computer’s personal store with a ten-year default lifetime.4 Its key is the device key, TPM-bound where a TPM exists.5 The collector signs each report with that key; the service checks the signature and that the device is enabled in Entra and managed by Intune.
dsregcmd /status | Select-String 'AzureAdJoined|DomainJoined|DeviceId|Thumbprint|KeyProvider|TpmProtected|DeviceAuthStatus'
Get-ChildItem Cert:\LocalMachine\My |
Where-Object { $_.Issuer -match 'CN=MS-Organization-Access' } |
Select-Object Subject, Thumbprint, NotAfter, HasPrivateKey02
Roll out with Intune
1. Where the module comes from
Nothing to package. Remediate-DoctorDO.ps1 installs DoctorDO.Collector from a PowerShell repository for all users, checks it before every import, and keeps it up to date. Detect-DoctorDO.ps1 also exits 1 when the module is missing or older than the minimum version, so a device that never had it is remediated on its first run.
| Step | What it does |
|---|---|
| Where | %ProgramFiles%\WindowsPowerShell\Modules\DoctorDO.Collector\<version>, in 64-bit PowerShell; the script relaunches itself in 64-bit, and Windows PowerShell 5.1 and PowerShell 7 both search that folder. |
| Which version | -UpdatePolicy LatestVerified (default): the newest in the repository, asked with Find-Module on every run, never below -MinimumVersion. Pinned: only the version stamped into the script. |
| Install | TLS 1.2, the NuGet provider if it is missing, then Install-Module -Scope AllUsers from -Repository (PowerShell Gallery by default). The repository’s trust setting is not changed. |
| Checks, before every import | Who can write to the module folder, that the files are exactly the release’s list, the SHA-256 of every file, and the module’s GUID, author and exported functions; optionally a signature by a certificate you name. |
| If a check fails | That copy is removed and the newest earlier version that passes is used. If none passes, nothing is loaded or sent, and the run exits 9 or 11. |
| Where the hashes come from | LatestVerified: collector-manifest.json from the project’s GitHub release, for any version but the stamped one. Pinned: the hashes stamped into the script. |
None of these requests carries device data. With an internal repository and Pinned, every one of them stays inside your network. The hashes show the files are the ones the release lists, not who made the release.DoctorDO’s reading
Optional: the module as a Win32 app
For an estate that wants Intune to own the module. Install-DoctorDOCollector.ps1 puts the same files in the same folder, and the remediation uses that copy if it passes the checks. Use one route per device group: the remediation updates over a Win32 copy, and the app’s detection rule can then report its version missing.
# .\package holds the released module folder, unchanged: DoctorDO.Collector\<version>\ # (Save-PSResource -Name DoctorDO.Collector -Path .\package, once the module is on a repository you can reach) Copy-Item collector\intune\Install-DoctorDOCollector.ps1 .\package IntuneWinAppUtil.exe -c .\package -s Install-DoctorDOCollector.ps1 -o .\out
| Setting | Value |
|---|---|
| Install command | powershell.exe -NoProfile -ExecutionPolicy Bypass -File Install-DoctorDOCollector.ps1 |
| Uninstall command | powershell.exe -NoProfile -ExecutionPolicy Bypass -File Install-DoctorDOCollector.ps1 -Uninstall |
| Install behavior | System |
| Detection rule | File DoctorDO.Collector.psd1 exists in %ProgramFiles%\WindowsPowerShell\Modules\DoctorDO.Collector\<version>; associated with a 32-bit app on 64-bit clients: No |
| Assignment | The same device group as the remediation |
2. Set your ingest endpoint in the remediation script
Intune passes no arguments to remediation scripts, so the settings live at the top of Remediate-DoctorDO.ps1, or in machine environment variables. Until the URL is set the script refuses to run and exits 3.
# Top of Remediate-DoctorDO.ps1: edit before uploading the script to Intune $ConfiguredIngestUri = 'https://<your ingest host>/v1/ingest' $ConfiguredDeviceAnchor = 'entra-device' # or 'tenant-ca'; must match your deployment
| Setting | Where | Default | Meaning |
|---|---|---|---|
$ConfiguredIngestUri | Script text | Placeholder (exit 3) | Exactly https://<host>/v1/ingest. The path is part of the signed request, so no other path or query string is accepted. |
DOCTORDO_INGEST_URI | Machine environment | Unset | Overrides the script text |
$ConfiguredDeviceAnchor | Script text | entra-device | entra-device or tenant-ca; anything else exits 3 |
DOCTORDO_DEVICE_ANCHOR | Machine environment | Unset | Overrides the script text |
-Repository | Script parameter default | PSGallery | The registered PowerShell repository the module is installed from; an internal one works |
-UpdatePolicy | Script parameter default | LatestVerified | LatestVerified or Pinned, as in the table above |
-UpdateCheckHours | Script parameter default | 24 | LatestVerified searches the repository at most this often while a usable version is installed; 0 searches every run |
-MaxJitterSeconds | Script parameter default | 600 | Random start delay, so a fleet on one schedule does not arrive in the same second |
3. Create the remediation
In the Intune admin center: Devices, Manage devices, Scripts and remediations, then create a remediation.1
| Field | Value | Why |
|---|---|---|
| Detection script | Detect-DoctorDO.ps1 | Checks only when the last report was accepted. It collects nothing. |
| Remediation script | Remediate-DoctorDO.ps1 | Installs or updates the module, checks it, then runs the collector and sends the report. |
| Run this script using the logged-on credentials | No | No runs the script in the system context.2 The device key is a machine key a user cannot use (exit 6), and reading Delivery Optimization state needs administrator rights (exit 1). |
| Enforce script signature check | Your policy | Yes only if you sign the scripts. |
| Run script in 64-bit PowerShell | Yes | The script also relaunches itself in 64-bit if started in a 32-bit host. |
| Schedule | Daily | Remediations can run once, hourly or daily.1 |
| Assignment | A device group | Entra joined or hybrid joined, Intune-enrolled devices. Do not mix user and device groups. |
Fallback: the tenant-ca anchor
Use this only if your deployment’s device anchor is tenant-ca. Each device then signs with an Intune SCEP or PKCS certificate from your own CA instead of the Entra device certificate.7 Requests are signed the same way.
- A device certificate, assigned to a device group, with the Entra device ID in the subject or SAN (
{{AAD_Device_ID}}). - Client Authentication EKU; RSA 2048 or EC P-256; TPM key storage where present.
- Set
$ConfiguredDeviceAnchor = 'tenant-ca', and register the issuing CA with DoctorDO once.
The full profile settings are in the collector’s Intune deployment notes, collector/intune/README.md.
03
Schedule
| What | When | Change it with |
|---|---|---|
| Remediation run | Daily | The remediation schedule in Intune |
| Detection window | 22 hours since the last accepted report | -MaxAgeHours in Detect-DoctorDO.ps1; shorter than the schedule, so a daily run always finds yesterday's report due; use 160 for a daily schedule that repeats every seven days |
| Start delay | Random, 0–600 seconds | -MaxJitterSeconds |
| Inventory report | First run, whenever the device’s inventory changes, and every 7 days | — |
| Telemetry report | Every run, after any inventory | — |
| After a refusal | HTTP 429: waits for Retry-After. 401, 408, 500, 502, 503 or 504, or a network error: backs off from 60 seconds, doubling, up to an hour. From the third failed run in a row a run that is still backing off exits 5, so Intune shows it | — |
04
What it collects, and what it never does
Collects
- Delivery Optimization configuration: each effective value and the channel that set it, plus the policy registry keys.
DOGroupIdis hashed with the fleet default-Anonymise, with a salt derived from your tenant, so equal Group IDs compare equal across devices;DOCacheHost, the Connected Cache server’s name, is sent as is. - Delivery Optimization state: bytes by source, cache and performance counters, peers and connections (aggregated; a peer’s address is hashed in the peer list and cut to a /24 or /48 network in the list of who served the bytes), and errors grouped by code.
- Connectivity: the result of each network check, and the Delivery Optimization firewall rules and proxy settings.
- Device and network facts: OS build, model, RAM, disk, TPM, chassis; subnet (reduced, for example
10.20.4.0/22), AD site, hashed gateway; the serial number only as a salted hash. - Autopilot provisioning: what the Enrollment Status Page tracked for up to 30 Win32 apps, by Intune app ID, and a summary of what Delivery Optimization downloaded around provisioning, in the inventory.
- Identity: the Entra device ID, tenant ID and Intune device ID, in the inventory. The device certificate in each request also carries the Entra device ID.
Never collects
- Who is signed in. Where a user name appears in text it keeps, in log lines and firewall rules, it is replaced first: profile paths such as
C:\Users\<name>(a name with a space too, and UNC and forward-slash forms),DOMAIN\userandAzureAD\user, e-mail addresses and UPNs. Log lines are unstructured, so an unusual form can get through. - The host name, when anonymising (the fleet default). With
-Anonymiseoff, LAN and group peers appear as full addresses in the list of who served the bytes, and IPv4 addresses stay in log text. - Credentials or tokens. There is no key or secret anywhere in the scripts or the module.
- The primary user, Entra group membership, Intune compliance state, or apps and policies other than Delivery Optimization. The processor reads compliance state and owner type from Microsoft Graph to check that a device is managed, and stores them; no finding uses them.
- The ingest service’s response body: only the status code is read.
What it never changes, and what it writes
- No policy writes, no firewall changes, no cache clearing, no service restarts.
- Fleet runs never turn on verbose logging and never flush the Delivery Optimization log, because a flush stops the service before reading the logs.6
- The collector writes only
HKLM\SOFTWARE\DoctorDO(LastSuccessfulCollection,LastReportKind) and its state folder%ProgramData%\DoctorDO(its state file and caches), readable by SYSTEM and Administrators only. - The remediation installs the module under
%ProgramFiles%\WindowsPowerShell\Modules, and the NuGet provider if it is missing, and writesDoctorDO-Remediation.login the Intune Management Extension’s log folder.
05
Exit codes
| Code | Meaning | Action |
|---|---|---|
| 0 | Success, or still backing off after an earlier run failed (HTTP 429, 401, 408, 500, 502, 503 or 504, or a network error); nothing was sent this run | None for one or two runs. From the third failed run in a row a run that is backing off exits 5; see Troubleshooting |
| 1 | Not elevated | Set “Run this script using the logged-on credentials” to No |
| 2 | Not running on Windows | The collector runs only on Windows. An older Windows build does run, and marks what it cannot read as not collected |
| 3 | Collection failed, or the script is not configured | Check $ConfiguredIngestUri and $ConfiguredDeviceAnchor, then the run output |
| 4 | Verbose logging could not be restored | Not used in fleet mode: fleet runs never enable verbose logging |
| 5 | Upload failed | The network path or proxy to your ingest host, or a report the service refused: the output gives the HTTP code. After a 429, 401, 408, 500, 502, 503 or 504, or a network error, the device backs off; after a 422 (the device clock ahead) it does not. The next run retries with the same counters |
| 6 | The device cannot identify itself | entra-device: not Entra joined or hybrid joined, no usable device certificate, or the device key could not sign. Run dsregcmd /status as SYSTEM and check AzureAdJoined, DeviceId, Thumbprint and DeviceAuthStatus; the output line says which. tenant-ca: the certificate profile has not reached this device. Either way a configuration state, not a bug. |
| 7 | The state folder is not safe to use | %ProgramData%\DoctorDO is a link, has another owner, inherits its permissions, or holds something the collector did not make. Nothing was read or sent. Delete the folder as an administrator; the next run makes it again for SYSTEM and Administrators only |
The remediation’s own codes, about the module
| Code | Meaning | Action |
|---|---|---|
| 8 | The module is missing or too old and could not be installed | During the preview the module is not on the PowerShell Gallery: set -Repository to an internal repository that has it, or deploy it as a Win32 app. Otherwise the log names the failed step (NuGet provider, Find-Module, Install-Module, proxy, TLS) |
| 9 | The module failed verification, and no earlier verified version is installed | The log names the check (a file differs, is extra or missing, the GUID or author is wrong, or other accounts can write to the folder). Treat a hash failure on a release as a possible supply-chain event until it is explained |
| 10 | The verified module could not be imported | The log has the import error; a copy that was just installed is removed |
| 11 | No version could be verified, and none is installed | collector-manifest.json could not be read or did not match (a private repository, no route to the release host). Host it yourself with -ManifestUrlTemplate, or use -UpdatePolicy Pinned |
Codes 0 to 7 come from the collector; 8 to 11 from the remediation, before the collector runs. Both scripts log to DoctorDO-Remediation.log in %ProgramData%\Microsoft\IntuneManagementExtension\Logs, which Intune’s Collect diagnostics gathers.
06
Troubleshooting
| What you see | Likely cause | Check |
|---|---|---|
| Exit 6 on some devices only | Entra registered (BYOD) or domain-only devices in the assignment | Assign to a device group of Entra joined or hybrid joined devices |
| Exit 1 everywhere | The script is running as the signed-in user | “Run this script using the logged-on credentials” = No |
| Exit 3 everywhere, “not configured” | The ingest URL is still the placeholder, or has a different path | $ConfiguredIngestUri |
| Exit 8 everywhere | The remediation could not install the module: with the default settings it looks on the PowerShell Gallery, where the preview module is not published | -Repository pointing at an internal repository that has the module, or the Win32 app; DoctorDO-Remediation.log |
| Exit 9 or 11 | The module failed its checks, or no release manifest could be read for the version wanted | DoctorDO-Remediation.log names the check; for 11, -ManifestUrlTemplate or -UpdatePolicy Pinned |
| Exit 3 on some devices | The collection failed, for example a required field could not be read | The remediation’s output and DoctorDO-Remediation.log |
| HTTP 401 in the output, exit 5 then 0, and the device never appears | The service refused the device: it is disabled or deleted in Entra, its clock is more than five minutes behind the service’s, or the deployment uses the other anchor. The run that gets the 401 exits 5 and backs off; a run during the back-off exits 0, and 5 again from the third failed run in a row | dsregcmd /status, the device clock, $ConfiguredDeviceAnchor |
| Exit 5, and the output says HTTP 422 | Usually the device’s clock is more than five minutes ahead of the service’s, so the report looks as if it comes from the future. There is no back-off: every run tries again | The device’s time, time zone and time synchronisation |
| Exit 5 | The device cannot reach your ingest host | Proxy and firewall rules for the ingest host from the device, as SYSTEM |
# On a test device, elevated (use psexec -s to run as SYSTEM, as Intune does) Import-Module DoctorDO.Collector Invoke-DoctorDOCollector -Fleet -IngestUri https://<your ingest host>/v1/ingest
07
Is a device reporting?
- In Intune, the remediation’s device status shows the detection output, for example “Compliant: last successful DoctorDO collection is inside the 22 h window.” Recurring scripts report within their first six days only when the result changes, so this can lag by up to a week:1 use the registry value or the Devices page for a same-day answer.
- On the device,
LastSuccessfulCollectionis written only after the service accepted a report.PowerShell · last accepted report Get-ItemProperty -Path 'HKLM:\SOFTWARE\DoctorDO' | Select-Object LastSuccessfulCollection, LastReportKind
- In DoctorDO, the device is listed in Devices with a recent “Seen” time.
One device, without sending anything
The same module, run by hand, writes New-DoctorDODeviceReport’s one HTML page for this device, which opens in Edge or Chrome with nothing deployed, or a bundle (a zip) for support. A bundle flushes the Delivery Optimization log first, which stops the service briefly;6 add -NoFlush to skip that.
# One HTML page for this device, the console's device page, that opens in Edge or Chrome. Nothing is sent. New-DoctorDODeviceReport -Open -Anonymise # A bundle (a zip) for support. Nothing is sent. Invoke-DoctorDOCollector -OutputPath C:\Temp -Anonymise
Microsoft documentation
References
- 1Use Remediations to detect and fix support issues (opens in a new tab)learn.microsoft.com/intune/device-management/tools/deploy-remediations
- 2Use PowerShell scripts on Windows devices in Intune (opens in a new tab)learn.microsoft.com/intune/device-management/tools/run-powershell-scripts-windows
- 3Troubleshoot devices by using the dsregcmd command (opens in a new tab)learn.microsoft.com/entra/identity/devices/troubleshoot-device-dsregcmd
- 4Microsoft Entra device management FAQ (opens in a new tab)learn.microsoft.com/entra/identity/devices/faq
- 5How Microsoft Entra device registration works (opens in a new tab)learn.microsoft.com/entra/identity/devices/device-registration-how-it-works
- 6Monitor Delivery Optimization (opens in a new tab)learn.microsoft.com/windows/deployment/do/waas-delivery-optimization-monitor
- 7Create and assign SCEP certificate profiles in Intune (opens in a new tab)learn.microsoft.com/intune/device-configuration/certificates/scep-profiles
- 8Enable Windows diagnostic data and license verification (opens in a new tab)learn.microsoft.com/intune/privacy/enable-windows-diagnostic-data#enable-windows-license-verification