IAMinerva
HomeBlogAbout
m3M365 NewscoMicrosoft CopilotteMicrosoft TeamsshSharePoint & OneDriveinIntune & SecurityexExchange & OutlookpoPower PlatformazAzure & Entra IDtuTutorials & GuidesevEvents & ConferencesseSecuritywiWindows
IAMinerva

Professional blog dedicated to the Microsoft 365 ecosystem.

Quick links

HomeBlogAboutNewsletter

Stay informed

Get the latest Microsoft 365 news delivered straight to your inbox.

© 2026 IAMinerva. All rights reserved.

Built withNext.js&Tailwind
Detecter les canaux inactifs Teams avec PowerShell
BlogMicrosoft TeamsDetect Inactive Teams Channels with PowerShell
Microsoft Teams#Teams#PowerShell#Microsoft Graph

Detect Inactive Teams Channels with PowerShell

Detect inactive Teams channels with Microsoft Graph PowerShell SDK. Scripts, automated archiving, and Azure Automation integration for data governance.

Houssem MAKHLOUF
June 25, 2026
9 min read

TL;DR par Minerva

généré par IA

Detect inactive Teams channels with Microsoft Graph PowerShell SDK. Scripts, automated archiving, and Azure Automation integration for data governance.

Introduction

Microsoft Teams environments gradually accumulate obsolete channels over time. These channels, created for temporary needs or completed projects, often remain orphaned without administrative intervention. This situation degrades user experience, pollutes search results, and in the context of Microsoft 365 Copilot, risks contaminating AI-generated responses with obsolete information extracted from archived conversations. This article addresses a systematic strategy for detecting these inactive channels and implementing a recurring management process.

Limitations of native Teams administration tools

Absence of inactivity reports in the admin center

Microsoft does not provide a native report to precisely identify inactive channels in the Teams administration interface. The standard Teams usage report includes an "active channels" column that is of limited usefulness, as it does not reveal which specific channels exhibit prolonged inactivity. Attempts to use Copilot for Microsoft 365 in the context of Teams administrative management return guidance toward "inactive collaboration" features that do not address the real need for granular identification of dormant channels.

Limitation of available metrics

The standard collaborative dashboard in the Teams admin center provides only global aggregates, without per-channel detail. This gap requires a programmatic approach using Microsoft Graph APIs to extract and analyze detailed activity data.

Solution architecture: Microsoft Graph PowerShell SDK

Technical prerequisites and permissions

The implementation requires an app-only session with the Microsoft Graph PowerShell SDK configured with the following permissions:

  • Team.ReadBasic.All: enumeration of Teams teams
  • Channel.ReadBasic.All: reading channel metadata
  • ChannelMessage.Read.All: access to message history
  • ChannelMember.Read.All: retrieval of channel membership
!

Important: App-Only mode required

A delegated session will not work, as it would require the authenticated account to be a member of all channels (shared, private, and standard) in the tenant. This situation is practically impossible to achieve in production. You must use an application registered in Microsoft Entra ID with admin consent applied to app-only permissions.

Inactivity detection logic

The detection algorithm works according to the following steps:

  1. Enumeration of teams via Get-MgTeam
  2. Iteration over channels of each team with Get-MgTeamChannel
  3. Extraction of the last message from the channel via Get-MgTeamChannelMessage
  4. Filtering of system messages: exclusion of automatic notifications (member additions, configuration changes, etc.)
  5. Temporal comparison: if the last human message is older than a defined threshold (180 days by default), the channel is marked as inactive
✦

Tip: Differentiation of human vs system messages

System messages posted automatically by Teams (membership notifications, configuration changes, etc.) do not reflect actual activity. Filtering based on the messageType property of the message is essential to correctly identify the last genuine user engagement.

Implementation of the detection script

Initialization and configuration

⚡PowerShell
1# Configuration of inactivity parameters
2$InactivityThresholdDays = 180
3$CutoffDate = (Get-Date).AddDays(-$InactivityThresholdDays)
4
5# App-only authentication
6Connect-MgGraph -ClientId "<APP_ID>" -TenantId "<TENANT_ID>" -CertificateThumbprint "<THUMBPRINT>"
7
8# Variables for report accumulators
9$InactiveChannels = @()

Enumeration and detection loop

⚡PowerShell
1# Retrieve all teams
2$Teams = Get-MgTeam -All
3
4foreach ($Team in $Teams) {
5 Write-Host "Processing team: $($Team.DisplayName)" -ForegroundColor Cyan
6
7 # Enumeration of channels (standard, shared, and private)
8 $Channels = Get-MgTeamChannel -TeamId $Team.Id -All
9
10 foreach ($Channel in $Channels) {
11 # Retrieve channel messages (limited to 1 for performance)
12 $Messages = Get-MgTeamChannelMessage -TeamId $Team.Id -ChannelId $Channel.Id `
13 -PageSize 1 -All | Where-Object { $_.MessageType -ne 'systemEventMessage' }
14
15 if ($null -eq $Messages -or $Messages.Count -eq 0) {
16 $LastMessageDate = $null
17 $LastMessageAuthor = "No messages"
18 }
19 else {
20 $LastMessage = $Messages[0]
21 $LastMessageDate = $LastMessage.CreatedDateTime
22 $LastMessageAuthor = $LastMessage.From.User.DisplayName
23 }
24
25 # Inactivity detection
26 if ($null -eq $LastMessageDate -or $LastMessageDate -lt $CutoffDate) {
27 $InactiveChannels += [PSCustomObject]@{
28 TeamName = $Team.DisplayName
29 TeamId = $Team.Id
30 ChannelName = $Channel.DisplayName
31 ChannelId = $Channel.Id
32 LastMessageDate = $LastMessageDate
33 LastMessageAuthor = $LastMessageAuthor
34 ChannelType = $Channel.DisplayName -match '^General$' ? 'Standard' : 'Shared/Private'
35 DaysInactive = if ($null -ne $LastMessageDate) {
36 ((Get-Date) - $LastMessageDate).Days
37 } else {
38 'N/A'
39 }
40 TeamOwners = @(Get-MgTeamOwner -TeamId $Team.Id).User.UserPrincipalName -join '; '
41 }
42 }
43 }
44}
45
46# Export and report
47$InactiveChannels | Export-Csv -Path "InactiveChannels.csv" -NoTypeInformation -Encoding UTF8
48Write-Host "$($InactiveChannels.Count) inactive channels detected" -ForegroundColor Yellow

Generating audit reports

Export to Excel with formatting

⚡PowerShell
1# Installation of module for Excel
2Install-Module ImportExcel -Force
3
4# Creation of an Excel workbook with formatting
5$ExcelParams = @{
6 Path = "TeamsInactiveChannelsReport.xlsx"
7 WorksheetName = "Inactive Channels"
8 AutoSize = $true
9 TableStyle = "Medium3"
10 FreezeTopRow = $true
11}
12
13$InactiveChannels | Select-Object TeamName, ChannelName, LastMessageDate, `
14 DaysInactive, LastMessageAuthor, TeamOwners | `
15 Export-Excel @ExcelParams
16
17Write-Host "Report generated: TeamsInactiveChannelsReport.xlsx"

Distribution of report by email

⚡PowerShell
1# Configuration of report sending
2$EmailParams = @{
3 To = "teams-admins@contoso.com"
4 From = "automation@contoso.com"
5 Subject = "Teams Activity Report - Inactive Channels $(Get-Date -Format 'yyyy-MM-dd')"
6 Body = @"
7Analysis report of inactive Microsoft Teams channels.
8
9Number of inactive channels (>180 days): $($InactiveChannels.Count)
10Report date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
11
12See attachment for detailed list.
13"@
14 Attachments = @("TeamsInactiveChannelsReport.xlsx")
15 SmtpServer = "smtp.office365.com"
16 Port = 587
17 UseSsl = $true
18 Credential = (Get-Credential)
19}
20
21Send-MailMessage @EmailParams

Management of detected inactive channels

Secure archiving of channels

Archiving is the recommended approach as it preserves history and conversations for compliance and audit:

⚡PowerShell
1foreach ($InactiveChannel in $InactiveChannels) {
2 try {
3 # Archiving of the channel via Microsoft Graph
4 Invoke-MgArchiveTeamChannel -TeamId $InactiveChannel.TeamId `
5 -ChannelId $InactiveChannel.ChannelId
6
7 Write-Host "✓ Archived: $($InactiveChannel.ChannelName) in $($InactiveChannel.TeamName)" `
8 -ForegroundColor Green
9 }
10 catch {
11 Write-Error "✗ Error archiving $($InactiveChannel.ChannelName): $_"
12 }
13}

Deletion of channels (destructive approach)

Deletion is irreversible and should only be used for test or temporary channels:

⚡PowerShell
1# Selective deletion with confirmation
2$ChannelsToDelete = $InactiveChannels | Where-Object { $_.DaysInactive -gt 365 }
3
4foreach ($Channel in $ChannelsToDelete) {
5 $Confirmation = Read-Host "Delete channel '$($Channel.ChannelName)'? (Y/N)"
6
7 if ($Confirmation -eq 'Y') {
8 Remove-MgTeamChannel -TeamId $Channel.TeamId -ChannelId $Channel.ChannelId
9 Write-Host "✓ Deleted: $($Channel.ChannelName)" -ForegroundColor Green
10 }
11}
×

Warning: Irreversible deletion

The Remove-MgTeamChannel cmdlet permanently deletes the channel and all its conversations. No recycle bin or recovery point exists for Teams. Perform a backup of the content via export before deletion.

Recurring automation with Azure Automation

Runbook configuration

To execute this detection on a regular basis (weekly or monthly), implement an Azure Automation runbook:

1

Create the Automation account

In the Azure portal, create an Azure Automation account with a managed identity having appropriate Graph permissions consented via Entra ID.

2

Import the Graph module

In the Automation account, import the Microsoft.Graph.Teams module from the Azure modules catalog.

3

Create the PowerShell runbook

Create a runbook of type PowerShell Workflow or Python 3.8+ containing the detection script. Configure authentication with the managed identity:

⚡PowerShell
1$connectionName = "AzureRunAsConnection"
2try {
3 $servicePrincipalConnection = Get-AutomationConnection -Name $connectionName
4
5 Connect-MgGraph -ClientId $servicePrincipalConnection.ApplicationId `
6 -TenantId $servicePrincipalConnection.TenantId `
7 -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
8}
9catch {
10 throw "Unable to connect to Azure Automation: $_"
11}
4

Configure the schedule

Create a schedule (Schedule) to run the runbook every Monday at 02:00 UTC and associate it with the runbook via a schedule link.

5

Configure notifications

Add an Azure Monitor Action Group action to send reports by email after each execution, using the runbook outputs.

Managed configuration variables

⚡PowerShell
1# Define these variables in the Automation account for flexibility
2$InactivityThreshold = Get-AutomationVariable -Name "TeamsInactivityDays"
3$ReportRecipient = Get-AutomationVariable -Name "ReportEmailAddress"
4$StorageAccountName = Get-AutomationVariable -Name "AuditStorageAccount"

Considerations on indexing and Copilot

Impact on search and generative AI

Conversations in inactive channels continue to be indexed by Microsoft 365 search services. When Microsoft 365 Copilot generates responses based on organizational context:

  • Obsolete data from old channels may be included in results
  • Outdated team/project statuses introduce imprecision
  • Project-related context conversations create noise

Regular archiving of inactive channels mitigates this data contamination, ensuring that Copilot relies on relevant and current information.

i

Good to know

Archiving does not remove existing indexing. Conversations remain searchable but are marked as archived. For complete isolation of obsolete data from Copilot, deletion remains the only recourse, with associated risks.

Recommended governance strategy

Validation process before action

  1. Monthly execution of the detection script
  2. Manual review of the report by team owners (notification via email)
  3. 15-day claim period: team owners can request channel retention
  4. Automated archiving of unclaimed channels via the runbook
  5. Retention of audit logs of all actions for compliance

Audit controls in the Microsoft 365 activity log

⚡PowerShell
1# Search for Teams channel archiving in audit logs
2Search-UnifiedAuditLog -RecordType TeamsAdmin -Operations ArchiveTeamChannel `
3 -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) |
4 Select-Object UserIds, CreationDate, ObjectId |
5 Export-Csv -Path "TeamsArchiveAudit.csv"

Troubleshooting common issues

Error: "Insufficient privileges to complete the operation"

Verify that:

  • The Entra ID application has admin consent applied
  • Graph permissions are consented at the tenant level (not just user)
  • The managed identity of Azure Automation has the necessary roles

Performance: Script slow on large tenants

To optimize on 10,000+ teams:

⚡PowerShell
1# Use pagination and parallelization
2$Teams = Get-MgTeam -All -PageSize 999
3
4# Parallel processing with runspace pools
5$RunspacePool = [runspacefactory]::CreateRunspacePool(1, 4)
6$RunspacePool.Open()

"System" messages are not correctly filtered

Verify the exact schema of the message type:

⚡PowerShell
1# Diagnostic: examine all message types
2$Messages = Get-MgTeamChannelMessage -TeamId $TeamId -ChannelId $ChannelId -All
3$Messages | Group-Object -Property MessageType | Select-Object Name, Count

Conclusion and next steps

The systematic identification of inactive channels in Microsoft Teams is an essential element of data governance. By implementing an automated process via Microsoft Graph PowerShell SDK and Azure Automation, you maintain a clean and performant collaborative environment while preserving data integrity for compliance and Copilot.

The complete script is available on the GitHub Office365itpros for download and reuse. Adapt the inactivity thresholds, permissions, and notification workflows according to your specific organizational policies.

Share:
HM

Houssem MAKHLOUF

Microsoft 365 enthusiast & IT professional.

Previous article

Identify Inactive Active Directory Users with PowerShell

Jun 25, 2026
Next article

Add Agendas and Meeting Notes to Teams with Loop

Jun 25, 2026

Related articles

Main d'homme interagissant avec une interface numérique lumineuse et dynamique.copilot

Agents: Transforming Work with AI in Microsoft 365

Intelligent agents are redefining work in Microsoft 365 by automating complex and extended tasks. Discover their impact and adoption.

Jun 28, 20263 min
Exécution de scripts PowerShell pour auditer des applications AI et gérer leurs enregistrements.copilot

Audit and Manage AI Applications with PowerShell

Audit unauthorized AI applications in Entra ID with PowerShell and Microsoft Graph to strengthen control and security.

Jun 28, 20264 min
Graphiques abstraits et géométriques avec des couches de couleurs translucides.exchange

Converting Exchange IDs for Microsoft 365 Graph API

Convert Exchange identifiers (storeId, entryId, RestId) for Graph API and targeted eDiscovery. Technical guide with complete PowerShell scripts.

Jun 28, 20267 min