What changed since last week

19 Oct 2026

What changed in my tenant since last week?

Every Microsoft admin centre shows you the state of your tenant: the devices, the roles, the consents as they are right now. None of them answers the question you actually have on a Monday morning, which is what moved since the last time you looked. Comparing two exports by hand is possible and nobody does it twice.

The GraphPaaS connector for Claude has a tool for that question, compare_snapshots. It takes two stored syncs of one domain and returns the rows added, removed and changed. That sounds like a plain diff. It isn't quite one, and the places where it isn't are where a diff of tenant data usually goes wrong. Everything below was asked against Fabrikam HQ, our demo tenant (synthetic people, real tool calls, latest sync 18 October at 05:45 UTC).

36
devices compared
36
"changed" by a naive diff
2
changed once the noise is out
1
rename that read as leaver + joiner

"Last week" is a snapshot, not a date

You ask

What changed on our devices since last week?

Claude calls
compare_snapshots(domain="devices", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
                  since="2026-10-11", limit=5)
→ from 2026-09-18 to 2026-10-18 · key device_id
→ 0 added · 0 removed · 1 changed · 35 unchanged
Claude answers

One device moved: FBK-IOS-017 went from compliant to noncompliant. Nothing was enrolled or removed. The comparison starts on 18 September, the newest stored snapshot on or before the day you asked about.

The tool never compares against a date. since and until each pick the newest stored snapshot on or before that day, and the answer says which two it picked. The demo keeps eight snapshots of devices between 14 September and 18 October, so "last week" landed on 18 September. A tenant that syncs every day lands on the day itself. Read the from line before reading the result.

The one change is the kind worth acting on. A device turns noncompliant as soon as it checks in and fails a policy, unless the policy sets a grace period on the Mark device noncompliant action. If Conditional Access requires compliant devices, its owner lost access at that check-in. The admin centre shows the device as noncompliant today. It doesn't show that it was compliant last week.

A row is whatever its id says it is

Before anything can be "changed", the tool has to decide that a row in the old snapshot and a row in the new one are the same thing. It matches on the id columns the domain has (device_id, upn, app_id, id and so on), lower-cased, because a re-cased UPN or GUID is still the same object. Some domains have no single id, and the tool refuses rather than guess:

You ask

Did anything change in governance since last week?

Claude calls
compare_snapshots(domain="governance", tenant_id="ce48e256-…", since="2026-10-11")
→ 400: 'governance' rows have no id column — pass key with the columns that name a row.
compare_snapshots(..., key=["kind", "role", "member_upn"])
→ 12 unchanged · warning: 24 shared an id with another row
compare_snapshots(..., key=["kind", "role", "member_upn", "ca_name", "app_name", "risky_upn"])
→ 0 added · 0 removed · 0 changed · 24 unchanged
Claude answers

No change. All 24 rows (admins, Conditional Access policies, expiring secrets and risky users) match the 18 September snapshot.

The middle call is the one to look at. With a key that only names admins, the Conditional Access and secret rows collide on empty values, and "12 unchanged" would have been a confident answer about half the data. The warning is there so that never passes as a clean result.

The opposite problem is a key that names too much. In licence_waste the id columns are the object id and the UPN, so a rename doesn't look like a change:

You ask

Who joined or left the licensed users since the 14th?

Claude calls
compare_snapshots(domain="licence_waste", tenant_id="ce48e256-…", since="2026-10-14")
→ 1 added (Liam O'Hara) · 1 removed (Liam Moreira) · 41 changed
   both rows: azure_object_id d3m0-user-0011
compare_snapshots(..., key=["azure_object_id"], watch=["upn", "display_name", "assigned_products"])
→ 0 added · 0 removed · 1 changed · 41 unchanged
Claude answers

Nobody joined or left. The added and removed rows are the same account: Liam Moreira was renamed to Liam O'Hara, with a new UPN and the same licences.

Microsoft's own guidance on UPN changes is to treat the object ID as the immutable identifier, not the UPN or the email address. The diff follows the same rule when you give it the key. Without it, a leaver report built on this would have sent someone to offboard a person who still works there.

WHERE A ROW LANDS IN COMPARE_SNAPSHOTS since snapshot newest on or before until snapshot default: latest sync match on key id columns, lower-cased no id → 400 shared ids → warning added only in the new snapshot removed log domain: aged out, not deleted in both · diff watched fields lists as sets, the rest from → to equal differs unchanged counted only changed + changed_fields Rename with the UPN in the key: one removed + one added. Key on the object id instead.

The column that changes on every row

Widen the window to the oldest stored day and the same 36 devices tell a different story:

You ask

And since mid-September?

Claude calls
compare_snapshots(domain="devices", tenant_id="ce48e256-…", since="2026-09-14", limit=5)
→ 36 changed · 0 unchanged
→ changed_fields: enrolled_at 36, last_sync 36, enrollment_type 21, join_type 12,
                  grace_period_ends 5, user 1, compliance_state 1
compare_snapshots(..., watch=["compliance_state", "user", "os_version", "encrypted"])
→ 2 changed · 34 unchanged
Claude answers

Two devices changed in a way that matters. FBK-IOS-017 is the same compliance drop as before. FBK-AND-012's user went from liam.moreira to liam.o'hara, the rename again, seen from the device side.

Every device "changed", and almost none of it is news. changed_fields counts the changed rows per column, and a column that moved on all 36 rows is nearly always a timestamp. In the demo, the dates move because the synthetic devices are regenerated. In your tenant it is last_sync, which Microsoft defines on the managedDevice resource as the last time the device completed a successful sync with Intune. It moves whenever the device checks in.

watch is the fix: only the listed columns count as a change. The first call tells you what to drop; the second asks the real question. The gap between 36 and 2 is what a diff without a watch list buries.

Lists compare as sets, and a log forgets on purpose

Some columns hold a list: the products a user is assigned, the scopes an app was consented, the methods a user registered. Microsoft stores a delegated consent as one space-separated scope string, and a string compare flags a reorder as a change. The tool splits those columns into items and reports the items added and removed instead of a before and after. So a new Mail.Read on a consented app would show up as just that, one scope. It knows MICROSOFT 365 E3+ENTERPRISE MOBILITY + SECURITY E3 is two products, split on the bare plus and not on the one inside a product name. In the demo windows above no list moved: with assigned_products watched, the rename was still the only change.

Log-shaped domains need the opposite caution:

You ask

Which sign-ins disappeared since the 14th?

Claude calls
compare_snapshots(domain="signins", tenant_id="ce48e256-…", since="2026-10-14", limit=2)
→ 0 added · 0 removed · 420 changed · changed_fields: when 420, user 6
→ rolling_window: true
Claude answers

None were deleted. Sign-ins are a rolling window: a removed entry aged out of it, it wasn't erased.

Entra keeps sign-in and audit logs for seven days on Free and 30 days on P1 or P2, so every sync drops the oldest entries. A diff that reads "removed" there as "deleted" tells you someone wiped the log. The flag stops Claude from saying that. (In the demo, the 420 entries are regenerated under the same ids, so they come back as changed on when. In a real tenant every sign-in has its own id, and the window moving forward shows up as added and removed.)

The implication
A diff of tenant data is only as honest as its key and its watch list. Ask it with the object id as the key and the columns you care about watched, and "what changed" is a two-line answer: one device out of compliance, one rename. Ask it without them and it is 36 changes and a leaver who never left. Trends tell you the direction (trends vs snapshots covers that). The diff tells you which row, and the stale devices audit covers what to do when the row is a device that stopped checking in.
Sources
GraphPaaS

Ask your own tenant what changed since last week, and get the rows, not a feeling.

Add the connector →

Newsletter

Get the next article by email

Practical M365 & Azure cost pieces like this one. No spam, unsubscribe anytime.