A governance audit is how you establish whether what's really configured in your tenant lines up with what you documented. In my experience there is almost always a gap — and that gap is seldom deliberate. It comes from incremental changes over time, admin turnover, quick fixes that hardened into permanence, and the slow drift that afflicts any living system left without periodic review.
Carrying out a governance audit needs neither specialist consultants nor costly tooling. What it needs is a structured approach, the right PowerShell cmdlets, and a readiness to act on what you uncover. This article supplies that structure.
Define the Audit's Scope
A complete Teams governance audit spans six areas: policy configurations, team ownership, guest and external access, sensitivity labels, lifecycle management and security settings. On a first audit, don't attempt to cover everything at once — prioritise the areas carrying the greatest risk exposure for your organisation. A financial services firm might put security settings and DLP first. A large enterprise might put team ownership and lifecycle management first. A healthcare organisation might put DLP and sensitivity labels first.
Audit Area 1: Policy Configuration Review
For every policy type (meeting, messaging, app permission, app setup, calling), confirm the following:
- What are the present settings of the Global (org-wide default) policy?
- Which named policies exist, what do their settings look like, and are those settings deliberate?
- To which users or groups is each named policy assigned?
- Do any named policies exist that aren't assigned to anyone (orphaned policies)?
- Are any named policies assigned straight to users who also sit inside a group assignment, producing a direct-over-group override that the rank table won't reveal?
- When was each named policy last modified, and does that date tie back to a change request, or is the change unaccounted for?
The PowerShell approach:
# Get all meeting policies and their key settings
Get-CsTeamsMeetingPolicy | Select-Object Identity, AllowCloudRecording, AllowTranscription, AutoAdmittedUsers
# Get all group policy assignments for meeting policies
Get-CsGroupPolicyAssignment -PolicyType TeamsMeetingPolicy | Sort-Object Rank
Audit Area 2: Team Ownership Checks
Team ownership is among the most frequently neglected corners of governance. The essential questions:
- How many teams count fewer than two owners?
- How many teams have no active owners at all (every owner has left the organisation)?
- How many teams have an ownership vacancy (an owner was removed but no replacement was assigned)?
# Find teams with zero or one owner
Get-Team | ForEach-Object {
$owners = Get-TeamUser -GroupId $_.GroupId -Role Owner
if ($owners.Count -lt 2) {
[PSCustomObject]@{
TeamName = $_.DisplayName
GroupId = $_.GroupId
OwnerCount = $owners.Count
Owners = ($owners.User -join ", ")
}
}
} | Export-Csv -Path "C:\Temp\LowOwnerTeams.csv" -NoTypeInformation
Audit Area 3: Guest and External Access Review
Questions for the guest access review:
- How many guest accounts are active in the tenant?
- When did each guest account last sign in?
- Of which teams is each guest a member?
- Are any guests members of teams carrying a "Confidential" sensitivity classification?
# Get all guest users and their last sign-in
Get-MgUser -Filter "userType eq 'Guest'" -Property DisplayName,UserPrincipalName,SignInActivity |
Select DisplayName, UserPrincipalName, @{N='LastSignIn';E={$_.SignInActivity.LastSignInDateTime}}
Audit Area 4: Sensitivity Label Review
For the governance of sensitivity labels:
- What share of teams have a sensitivity label applied?
- Are there teams left with no label (unclassified)?
- Do the label settings (guest access restrictions, external sharing settings) remain appropriate?
- Has any label-protected team been found with inappropriate guest access despite its label settings?
the compliance portal offers a label activity report showing how labels are distributed across groups and sites. Treat this as the main data source for the sensitivity label part of the audit.
Audit Area 5: Lifecycle Management Review
- Are cloud group expiration policies configured and working?
- How many teams have sat inactive for 90+ days?
- How many teams have sat inactive for 180+ days?
- Are archived teams being looked after (rather than left in limbo indefinitely)?
Audit Area 6: Security Settings Review
Cross-reference the security settings audit checklist in the dedicated article (article 09 of this series). The main items are: MFA enforcement status, Conditional Access policy coverage, the scope of external access federation, app permission policy settings, and DLP policy status.
Recording Findings and Acting on Them
An audit only creates value if its findings get acted on. Record findings under three categories:
- Critical: Must be remediated at once (ownerless teams holding sensitive content, misconfigured security settings).
- High: Needs remediation within 30 days (stale guest accounts, teams inactive for 180+ days, policy orphans).
- Medium: Warrants attention in the next governance cycle (teams with a single owner, unclassified teams, minor policy configuration drift).
Give each finding an owner and track remediation through to completion. An audit report left sitting in a folder with no follow-up is worse than no audit at all — it produces a record of known problems with no evidence of correction.
Repeating the Audit Six Months On
A governance audit of Teams is a comparison between the written standard and the tenant, plus a list of defects with owners assigned. Run once, it's a project. Run on a cadence, it's a control. It's the second run that reveals whether anyone actually did the work.
A professional body with roughly 500 staff finished a first audit in spring and logged 28 findings. The autumn run went over the same six areas using the same exports. Eleven findings were closed, nine were unchanged and eight were new, mostly teams spun up by a new membership app that hadn't existed in spring. The nine unchanged ones were the real result. They had been accepted in a meeting and then never assigned. Because it was long, the audit report had felt like progress.
Frame the second run so it begins from the previous finding list rather than a blank template. For each older finding, note closed, still open, or no longer applicable, along with the evidence. Then rerun the exports to catch new defects. A report that only describes the present state, with no reference to last time, can't answer the one question leadership cares about: are we tightening up or not.
Keep the exports the same shape so they can be diffed. Owner count, guest count, label, last activity and effective policy name, one row per team, suffices for the team-level half. Policy settings belong on a second sheet, one row per policy per setting you care about. Changing the columns between runs turns the comparison into a manual reading exercise, and manual reading is where findings get softened.
Severity should hold steady as well. If an ownerless team counted as critical in spring, it is critical in autumn. Reclassifying it as medium simply because there are fewer of them hides the ones still there. The counts can improve while the definition stays fixed.
Close the loop within the same quarter. An audit that hands over findings in week one and reviews them in week eight, with names attached only to the critical rows, will beat an audit that delivers a forty-page pack and a promise to "take it away". The pack can still exist. It is not the control. The dated closure is the control.
Keep the exports from both runs. A finding that can't be traced back to a row will get argued over rather than fixed. Separate defects in integrations from defects in teams people created. They answer to different owners. Where a finding is accepted as a risk, write down the acceptance, the role, and the review date. Silence does not equal acceptance. Don't grow the checklist on every run. Add an item only once a genuine miss has shown the current list is blind to it. Sample teams that look healthy as much as teams that look broken. A label that's applied and wrong is worse than a missing label, and a missing-label query won't catch it. Hand the next auditor the queries. An audit that lives only in someone's console history won't be repeated faithfully. Put the count of open critical findings on one line at the top. The narrative can come after. That line is what gets read. A policy that matches the standard but is assigned to nobody is not a pass. Flag it as unused and decide whether to keep it. Ownerless teams carrying a label for sensitive work outrank ownerless teams that are empty. Let the report say so. Book the next run before the current one is presented. A cadence that relies on spare time will slip. The second run ought to take less time than the first. If it doesn't, the exports are still too manual. Findings accepted as a risk should reappear on the next report by themselves, or acceptance turns into a way to delete them. A new integration since the last run deserves a section of its own, even if the rest of the tenant got better. Critical findings left open across two runs in a row belong on the first slide, ahead of anything that improved. Don't relabel severity to flatter the trend. Either change the tenant or leave the label alone. The queries and the report template belong in the same folder as the exports. Write the sample size down. A review of twenty teams is not a review of the tenant, and it shouldn't be titled as one. Where possible, owner names on findings should be roles, so the finding outlives a staff change.