Syncing Intune into Snipe-IT, including the part where devices leave
Every Snipe-IT install I’ve seen eventually drifts from reality. The truth about endpoints lives in Intune — what’s enrolled, who has it, whether it’s compliant — and the asset records live in Snipe-IT, maintained by whoever remembered to update them. Give it six months and you’re auditing against a database that’s 20% fiction.
Intune2snipe is my fix for that. It’s a Python CLI (MIT licensed) that pulls managed devices from Microsoft Graph and creates or updates the matching Snipe-IT hardware: serials, models, manufacturers, compliance state, custom fields, and checkout to whoever actually has the machine. Schedule it from cron, Docker, or a Kubernetes CronJob and stop maintaining the ledger by hand.
Plenty of scripts do some version of that, so let me focus on the two problems that took the most work, then the practical bits.
Devices leave, and that’s where asset databases rot
Writing a one-way sync is easy: devices flow from Intune into Snipe-IT and nothing ever comes back out. But real fleets churn. Laptops get wiped for the next hire, phones get retired, machines fall out of management. A sync that only adds gives you an asset database where a wiped, shelf-bound laptop still shows as deployed to someone who left in March.
Intune2snipe keeps a small JSON state file between runs with the serials it saw last time. On each run it compares, and devices that went missing get sorted rather than forgotten:
- Intune says a retire or wipe is in progress → status Pending Retire
- Gone from Intune, but the serial is still registered in Windows Autopilot — meaning it’s a company machine waiting to be re-deployed → Pending Autopilot
- Gone from both → Archived
- Shows back up in Intune later (re-enrolled after a reimage) → restored and synced like normal
That last transition matters more than it sounds. The wipe-and-reissue cycle is exactly when manual asset tracking falls apart, because the person doing the reimage is never the person maintaining Snipe-IT. With the Autopilot check in the loop, a laptop moves from “deployed to Alice” through “Pending Autopilot” to “deployed to Bob” without anyone touching the database.
The status label names are configurable, and they’re created automatically if your Snipe-IT doesn’t have them yet.
Checkout needs the user to exist first
The other recurring failure: the sync wants to check a device out to alice@example.com, and there’s no such user in Snipe-IT. Or worse, there are two, because Alice got married and her UPN changed, and a naive email match created a duplicate.
The newest feature is an optional pre-sync pass that pulls members of chosen Entra ID groups into Snipe-IT as users before any assets sync, creating their company, department, and location from Entra attributes along the way. Matching uses the Entra object ID (stashed in Snipe’s employee_num field) instead of email or username, so renames and domain migrations don’t fork identities.
export ENTRA_USER_SYNC_ENABLED=true
export ENTRA_USER_SYNC_GROUP_IDS=<group-object-id>
Related quality-of-life things that fell out of production use: Android Enterprise work profiles report the user with a 32-character GUID bolted onto the UPN, which gets stripped so checkout finds the real person. Primary-user lookups go through Graph $batch so large fleets don’t take an HTTP round-trip per device. Soft-deleted Snipe assets get restored instead of duplicated. Devices that haven’t checked into Intune for N days can be automatically checked in.
Running it
Config is entirely environment variables, so the same setup works everywhere. Start with a dry run — it logs every write it would make without touching anything:
git clone https://github.com/brngates98/intune2snipe.git
cd intune2snipe
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export AZURE_TENANT_ID=... AZURE_CLIENT_ID=... AZURE_CLIENT_SECRET=...
export SNIPEIT_URL=https://snipe.example.com/api/v1
export SNIPEIT_API_TOKEN=...
python3 app.py --dry-run --platform windows
For scheduled runs there’s a Docker image on GHCR (ghcr.io/brngates98/intune2snipe) and a Helm chart that deploys it as a nightly CronJob. If you’re using the lifecycle reconciliation, put SYNC_STATE_FILE somewhere persistent — it’s the memory between runs.
On the Azure side you need an app registration with application permissions: DeviceManagementManagedDevices.Read.All at minimum, plus Group.Read.All and User.Read.All if you use group filtering, user checkout, or the Entra pre-sync. Everything against Microsoft is read-only; the only thing that gets written to is Snipe-IT. The configuration guide has the full walkthrough, including the custom field mappings and the more obscure knobs.