The map
| Surface | Authoritative for | Blind spots |
|---|---|---|
| Teams admin center (TAC) | Day-to-day: policies UI, users, devices, TAC analytics | Bulk ops; several PowerShell-only parameters never render; UI lags policy writes |
| Teams PowerShell (MicrosoftTeams module) | Policies (all ~140 types), voice config, the parameters TAC hides | Group/site plumbing (that's Graph/EXO/SPO) |
| Microsoft Graph | Teams/channels/members as OBJECTS, automation at scale, app installs | Most policy TYPES aren't in Graph — the policy engine remains PowerShell country |
| M365 admin center | Licences, the underlying groups, org profile | Anything Teams-specific beyond activation |
Plus three neighbours that secretly manage Teams: Purview (retention/IB/eDiscovery), Entra (group creation policy, guest settings, CA), SPO admin (sharing limits that bind channel files). A "Teams setting" that seems missing is usually living in one of these.
Which one wins
They write to the same backing stores, so there is no merge — last write wins, regardless of surface. The interesting conflicts:
- TAC shows stale state: policy writes via PowerShell appear in TAC after cache lag (minutes). Writing in TAC while a script also writes = whoever saved last silently wins. Treat TAC as a viewer when automation owns a policy area.
- Same concept, two names: TAC friendly names vs PowerShell parameters
("Who can present" vs
DesignatedPresenterRoleMode). Our KB maps these — searching the tenant (Ask your tenant) resolves the real parameter every time. - Graph beta vs v1.0: beta exposes Teams things v1.0 doesn't; building production automation on beta is how you inherit someone's 2 a.m. incident.
The operating discipline
- Read before write, in the surface you'll write from.
- One owner per policy area — humans-in-TAC or code-in-PowerShell, never both.
- Export the truth on schedule — which is literally what serv365's scan does: every policy type, canonicalised, hashed, diffed. Drift between what you intended and what any surface wrote shows up as a change event with before/after.
What to watch (proofs)
- The authoritative value of anything:
Get-Cs<PolicyType> -Identity <name> | fl— PowerShell output IS the store; screenshot TAC only for UI-lag comparisons. - Who changed what from where: unified audit log operations for Teams admin actions record the actor — TAC clicks and PowerShell writes both land there; an unexpected actor id is your rogue-automation detector.
- Cache lag in action: change a policy in PowerShell, watch TAC show the old value for minutes — do it once deliberately so you stop trusting TAC freshness forever.
- Module hygiene:
Get-InstalledModule MicrosoftTeams— voice cmdlet behaviour shifts between versions; pin and upgrade deliberately, read release notes for removed cmdlets.