Entra / Microsoft 365 · Exchange Online
Fetch Message Center posts with Graph
Fetch Microsoft 365 Message Center service announcements using Graph and report posts by workload, including delayed updates.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -NoWelcome -Scopes ServiceMessage.Read.All
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-MgGraph -NoWelcome -Scopes ServiceMessage.Read.All$CSVOutputFile = "c:\temp\MessageCenterPosts.csv"# Fetch information from GraphWrite-Host "Fetching Microsoft 365 Message Center Notifications..."[array]$MCPosts = Get-MgServiceAnnouncementMessage -Sort 'LastmodifiedDateTime desc' -All# And Report what we findWrite-Host "Generating a report..."$Report = [System.Collections.Generic.List[Object]]::new()ForEach ($M in $MCPosts) {[array]$Services = $M.ServicesIf ([string]::IsNullOrEmpty($M.ActionRequiredByDateTime)) { # No action required date$ActionRequiredDate = $null} Else {$ActionRequiredDate = Get-Date($M.ActionRequiredByDateTime) -format "dd-MMM-yyyy"}# Get age of update$Age = New-TimeSpan($M.LastModifiedDateTime)$AgeSinceStart = New-TimeSpan($M.StartDateTime)# Trim the message text$Body = $M | Select-Object -ExpandProperty Body$HTML = New-Object -Com "HTMLFile"$HTML.write([ref]$body.content)$MCPostText = $HTML.body.innerText$ReportLine = [PSCustomObject] @{MessageId = $M.IdTitle = $M.TitleWorkloads = ($Services -join ",")Category = $M.category'Start Time' = Get-Date($M.StartDateTime) -format "dd-MMM-yyyy HH:mm"'End Time' = Get-Date($M.EndDateTime) -format "dd-MMM-yyyy HH:mm"'Last Update' = Get-Date($M.LastModifiedDateTime) -format "dd-MMM-yyyy HH:mm"'Action Required by' = $ActionRequiredDateMessageText = $MCPostTextAge = ("{0} days {1} hours" -f $Age.Days.ToString(), $Age.Hours.ToString())IsRead = $M.ViewPoint.IsReadIsDismissed = $M.ViewPoint.IsDismissedMinutesSinceUpdate = $Age.TotalMinutesMinutesSinceStart = $AgeSinceStart.TotalMinutes}$Report.Add($ReportLine) }$Report | Sort-Object {$_.'Last Update' -as [DateTime]} -Descending | `Select-Object MessageId, Title, Category, 'Last Update', 'Action Required By', Age | Out-GridView$Report | Export-CSV -NoTypeInformation $CSVOutputFileClear-Host# Figure out how many MC posts are for each workload[int]$TeamsMC = 0; [int]$ExchangeMC = 0; [int]$SharePointMC = 0; [int]$OtherMC = 0[int]$StreamMC = 0; [int]$PlannerDelays = 0; [int]$IntuneMC = 0; [int]$OneDriveMC = 0; [int]$WebAppsMC = 0[int]$VivaMC = 0; [int]$M365AppsMC = 0; [int]$M365SuiteMC = 0; [int]$DefenderMC = 0ForEach ($R in $Report) {Switch -wildcard ($R.Workloads) {"*Teams*" {$TeamsMC++}"Exchange*" {$ExchangeMC++}"SharePoint*" {$SharePointMC++}"*Viva*" {$VivaMC++}"Microsoft 365 apps" {$M365AppsMC++}"*Stream*" {$StreamMC++}"*Defender*" {$DefenderMC++}"*Planner*" {$PlannerMC++}"*OneDrive*" {$OneDriveMC++}"Microsoft 365 suite" {$M365SuiteMC++}"*Intune*" {$IntuneMC++}"*web*" {$WebAppsMC++}Default {$OtherMC++}}}# Figure out delays$DelayedPosts = $Report | Where-Object {$_.Title -like "*(Updated)*"}[int]$TeamsDelays = 0; [int]$ExchangeDelays = 0; [int]$SharePointDelays = 0; [int]$OtherDelays = 0[int]$StreamDelays = 0; [int]$PlannerDelays = 0; [int]$IntuneDelays = 0; [int]$OneDriveDelays = 0; [int]$WebAppsDelays = 0[int]$VivaDelays = 0; [int]$M365AppsDelays = 0; [int]$M365SuiteDelays = 0; [int]$DefenderDelays = 0ForEach ($Delay in $DelayedPosts) {Switch -wildcard ($Delay.Workloads) {"*Teams*" {$TeamsDelays++}"Exchange*" {$ExchangeDelays++}"SharePoint*" {$SharePointDelays++}"*Viva*" {$VivaDelays++}"Microsoft 365 apps" {$M365AppsDelays++}"*Stream*" {$StreamDelays++}"*Defender*" {$DefenderDelays++}"*Planner*" {$PlannerDelays++}"*OneDrive*" {$OneDriveDelays++}"Microsoft 365 suite" {$M365SuiteDelays++}"*Intune*" {$IntuneDelays++}"*web*" {$WebAppsDelays++}Default {$OtherDelays++}}}$PercentDelayed = ($DelayedPosts.count/$Report.count).toString('P')$PercentExchange = ($ExchangeDelays/$ExchangeMC).toString('P')$PercentIntune = ($IntuneDelays/$IntuneMC).toString('P')$PercentM365Apps = ($M365AppsDelays/$M365AppsMC).toString('P')$PercentM365Suite = ($M365SuiteDelays/$M365SuiteMC).toString('P')$PercentWebApps = ($WebAppsDelays/$WebAppsMC).toString('P')$PercentDefender = ($DefenderDelays/$DefenderMC).toString('P')$PercentOneDrive = ($OneDriveDelays/$OneDriveMC).toString('P')$PercentPlanner = ($PlannerDelays/$PlannerMC).toString('P')$PercentSharePoint = ($SharePointDelays/$SharePointMC).toString('P')$PercentStream = ($StreamDelays/$StreamMC).toString('P')$PercentTeams = ($TeamsDelays/$TeamsMC).toString('P')$PercentViva = ($VivaDelays/$VivaMC).toString('P')$PercentOther = ($OtherDelays/$OtherMC).toString('P')Write-Host ""Write-Host ("{0} message center posts analyzed - report data is available in {1}." -f $MCPosts.count, $CSVOutputFile)Write-Host ""Write-Host ("Number of delayed posts: {0} ({1})" -f $DelayedPosts.count, $PercentDelayed)Write-Host ""Write-Host "Delayed Posts by Workload"Write-Host "-------------------------"Write-Host ""Write-Host ("Exchange Online {0} ({1})" -f $ExchangeDelays, $PercentExchange)Write-Host ("Intune {0} ({1})" -f $IntuneDelays, $PercentIntune)Write-Host ("Microsoft 365 apps {0} ({1})" -f $M365AppsDelays, $PercentM365Apps)Write-Host ("Microsoft 365 suite {0} ({1})" -f $M365SuiteDelays, $PercentM365Suite)Write-Host ("Microsoft 365 for the web {0} ({1})" -f $WebAppsDelays, $PercentWebApps)Write-Host ("Microsoft Defender {0} ({1})" -f $DefenderDelays, $PercentDefender)Write-Host ("OneDrive for Business {0} ({1})" -f $OneDriveDelays, $PercentOneDrive)Write-Host ("Planner {0} ({1})" -f $PlannerDelays, $PercentPlanner)Write-Host ("SharePoint Online {0} ({1})" -f $SharePointDelays, $PercentSharePoint)Write-Host ("Stream {0} ({1})" -f $StreamDelays, $PercentStream)Write-Host ("Teams {0} ({1})" -f $TeamsDelays, $PercentTeams)Write-Host ("Viva {0} ({1})" -f $VivaDelays, $PercentViva)Write-Host ("Other workloads {0} ({1})" -f $OtherDelays, $PercentOther)
Attribution
Author
Office365itpros