Device Asset Sync
Every computer reports itself at sign-on, so the inventory stops drifting.
Problem
The organization had no reliable record of what computers it owned or where they were. The asset register was updated by hand. People moved, reassigned and renamed machines without updating it. An audit fixed the register once, and errors returned.
Approach
Each machine reports its own state to the register. The machine is the source of truth, and the register is a copy.
flowchart TB
subgraph Manual[by hand]
H1[a person moves a laptop] --> R1[record not updated]
R1 --> H2[the next person trusts the record]
H2 --> R2[the error compounds]
end
subgraph Loop[by the machine]
S[machine signs on] --> P[probe and compare]
P --> W[patch the difference]
W --> T[audit date is now]
T -.->|next sign-on| S
endThe script runs at user sign-on, when the machine's location and user are current. It writes only the fields that differ, which stays within the API rate limit and keeps people's correct edits.
Solution
The PowerShell script uses an OOP state machine pattern: abort conditions first (domain membership check catches discarded or stolen machines), then check if a record exists, check if it is accurate, update if needed, audit to log a valid timestamp. Deployed to run on sign-on via Group Policy.
flowchart LR
M[machine facts: serial, MAC, model, user, hostname] --> A{on the domain?}
A -->|no| STOP[stop, no write]
A -->|yes| F{placeholder serial?}
F -->|yes| MAC[MAC becomes the serial] --> Q1
F -->|no| Q1[exists? look up by serial]
Q1 --> Q2[model by name] --> Q3[user by username]
Q3 --> D{record against machine}
D -->|no record| C[create, note: please verify and tag] --> RR[run again]
D -->|fields differ| P[patch only the difference]
D -->|same| N[no write]
C --> AU[audit: timestamp]
P --> AU
N --> AUif (-not (Test-OnDomain)) { return } # discarded or stolen: never write
$f = Get-MachineFacts
if ($f.Serial -eq 'Default String') { $f.Serial = $f.Mac -replace ':','' }
$asset = Get-Register -Serial $f.Serial # exist?
if (-not $asset) { New-Asset $f -Note 'verify and tag'; & $PSCommandPath; return }
$delta = Compare-Fields $asset $f # what is inaccurate?
if ($delta.Count) { Update-Asset $asset.id $delta } # here is the info
Invoke-Audit $asset.id # heartbeat| Field | Register | Machine | Write |
|---|---|---|---|
| Serial | SN-0123 | SN-0123 | none |
| Model | Desktop 800 | Desktop 800 | none |
| User | user-a | user-b | patch |
| Hostname | PC-07 | PC-07 | none |
| Last audit | 40 days ago | signed on now | audit |
One field differs, so one field is written.
Synthetic values. The real script compares five fields.
A new record gets a note asking a technician to verify and tag the machine. Every run ends with an audit call that sets the record's audit date. A stale audit date means the machine has not signed on. Macs sync on a schedule from the Mac management service, with the same logic.
Outcome
Mac sync runs over 229 devices, September to October 2024:
| Run | Patched | Created | Audited |
|---|---|---|---|
| 1 | 198 | 9 | 198 |
| 2 | 199 | 8 | 199 |
| 3 | 198 | 7 | 198 |
| 4 | 213 | 10 | 213 |
| 5 | 213 | 10 | 213 |
| 6 | 54 | 4 | 218 |
The median run took 14 minutes 35 seconds. In one run, the counters reported 213 patches and 0 errors, but the register had rejected 215 of 223 writes. After that bug was fixed, patches fell to 54. In 2026, a register update removed the account's API rights, and every call returned 401.
Applicability
The pattern fits any fleet of devices that can report their own state, such as laptops, kiosks and instruments. Compare device state with the record, write only the differences, and set an audit timestamp on every run. Check write results against the server's responses, not the script's counters.