Training module · Draft
Module 3: Microsoft 365 Message center
Status — Draft
This page is still being written. Expect gaps and changes; it is not part of the workbook's production guidance. Last reviewed 2026-11-09.
Part of the Provider Notices learning path. Gate G2 Microsoft 365. Workbook Chapter 02. About 50 minutes.
The narrated deck for this module, and the chapter’s narrated tour, come later.
Introduction (3 minutes)
The Message center is where Microsoft announces changes, retirements and actions for a Microsoft 365 tenant; service health lists incidents and advisories. Each administrator’s view of the Message center is their own, and the “Act by” date is rare enough that Microsoft says to pay extra attention when it appears. Graph’s service communications API reads both, with two read permissions.
In this module you read the fixture tenant’s posts and issues, see which ones Microsoft said need action and which the kit’s rule marked, and watch the kit refuse to follow a next link off the tenant’s own Graph host. A GCC High tenant read on the worldwide host returns nothing, and an empty list looks like a quiet week.
The federal requirement. Gate G2 supports SA-9 c, SI-5 a and, for posts that announce a change, CM-3 b. The gate’s output is evidence for these controls; your organization still sets their parameters, policy and procedures, and the assessor decides whether each is satisfied. The guide’s Step 2 names them, and the workbook chapter’s Federal requirements and evidence table has a row for every lab step.
| Requirement | KSI | Evidence |
|---|---|---|
| SA-9 c | KSI-SCR-MIT, KSI-SCR-MON | Get-PnRequiredPermission -Provider m365: two read permissions; the consent record |
| SA-9 c; SI-5 a | KSI-SCR-MIT, KSI-SCR-MON | raw/m365.json, notices.json, manifest.json; every request on the tenant’s own Graph host |
| SI-5; CM-3 b (review proposed changes), through the tasks Module 7 routes | KSI-SCR-MON, KSI-CMT-LMC, KSI-CMT-RVP | Change and retirement notices with their actionBasis |
KSIs are FedRAMP 20x indicators for cloud service providers. The agency lines this evidence up with what its providers report against them; it doesn’t report KSIs itself. Which file supports which indicator is the program’s mapping (design guidance). The guide’s Evidence section explains both.
Learning objectives (1 minute)
After this module, you’ll be able to:
- name the two Graph permissions the reader needs, and the role data privacy messages need;
- say which Graph host each Microsoft 365 cloud uses, and why the wrong one fails silently;
- map Message center posts and service health issues to the record’s kinds;
- tell a date Microsoft set from the kit’s rule for a
preventOrFixIssuepost; - show that the kit refuses a next link on another host.
Prerequisites (1 minute)
- Module 1: the record and its action basis.
- Book 15, Chapter 03: why nothing is approved on the strength of an email.
Unit 1: What the gate asks for (7 minutes)
Read the guide’s Step 2: consent to the two permissions, sign in to the tenant’s own cloud (Global for GCC, USGov for GCC High, USGovDoD for DoD), and collect. Then read its Government-cloud availability and Interim path paragraphs, and the Validate (G2) callout: a post with an action-required date is action required with that date and the basis provider: actionRequiredByDateTime.
Unit 2: The Graph reader (10 minutes)
Read the workbook chapter in this order:
- What Microsoft gives you: the three list calls, their permissions and the national cloud hosts.
- What it leaves to you: categories and tags,
actionRequiredByDateTime, next links and a post’s end date. - The code: how each post and issue becomes a kind, and the session guard.
- Dangers.
Exercise: Lab 1, fixtures, no tenant (20 minutes)
This is the workbook’s Chapter 02, Lab 1, which explains each result. Work in examples/provider-notices, in PowerShell 7. The tenant is fictional, in gcc.
Every command in this lab, in order, from examples/provider-notices. Each step below says what it does and what to look for.
# Step 1: List what Microsoft 365 needs
Import-Module ./powershell/ProviderNotices/ProviderNotices.psm1
Get-PnRequiredPermission -Provider m365 | Format-Table Call, Permissions, Verified
# Step 2: See each cloud's Graph host
foreach ($c in 'gcc', 'gcchigh', 'dod') { $x = Get-PnCloud -Provider m365 -Cloud $c; '{0,-8} {1,-32} {2}' -f $c, $x.Graph, $x.GraphEnvironment }
# Step 3: Collect Microsoft 365 from the day-one fixtures
./Invoke-PnCollect.ps1 -SettingsPath tests/fixtures/settings.lab.json -FixturePath tests/fixtures/day1 -Provider m365 -AsOf 2026-11-09T12:00:00Z -OutputPath out/m365
# Step 4: Read the records
Import-Csv out/m365/notices.csv | Format-Table noticeId, kind, status, actionRequired, actionRequiredBy, title
# Step 5: See who said each one needs action
(Read-PnJson -Path out/m365/notices.json).notices | ForEach-Object { [pscustomobject]@{ noticeId = $_.noticeId; actionBasis = $_.actionBasis } } | Format-Table
# Step 6: Collect the second week and read what changed
./Invoke-PnCollect.ps1 -SettingsPath tests/fixtures/settings.lab.json -FixturePath tests/fixtures/day2 -Provider m365 -AsOf 2026-11-16T12:00:00Z -OutputPath out/m365-day2
Import-Csv out/m365-day2/notices.csv | Format-Table noticeId, kind, status, actionRequiredBy, title
# Step 7: Build the live request without sending it, then try a next link on another host
$s = Read-PnSettings -Path tests/fixtures/settings.lab.json
(Get-PnLiveCommand -CallId m365-messages -Settings $s).Uri
Get-PnLiveCommand -CallId m365-messages -Settings $s -Arguments @{ nextLink = 'https://graph.example.net/v1.0/admin/serviceAnnouncement/messages?skip=100' }
# Step 8: Run this chapter's tests
Invoke-Pester -Path tests/Collectors.Tests.ps1, tests/Live.Tests.ps1 -FullNameFilter 'Microsoft 365 service communications*', '*Graph*' -Output None -PassThru | Select-Object PassedCount, FailedCount
# After the steps
Remove-Item -Recurse -Force outList what Microsoft 365 needs:
Import-Module ./powershell/ProviderNotices/ProviderNotices.psm1 Get-PnRequiredPermission -Provider m365 | Format-Table Call, Permissions, VerifiedCall Permissions Verified ---- ----------- -------- m365-messages ServiceMessage.Read.All True m365-issues ServiceHealth.Read.All True m365-health-overviews ServiceHealth.Read.All TrueSee each cloud’s Graph host:
foreach ($c in 'gcc', 'gcchigh', 'dod') { $x = Get-PnCloud -Provider m365 -Cloud $c; '{0,-8} {1,-32} {2}' -f $c, $x.Graph, $x.GraphEnvironment }gcc https://graph.microsoft.com Global gcchigh https://graph.microsoft.us USGov dod https://dod-graph.microsoft.us USGovDoDCollect Microsoft 365 from the day-one fixtures:
./Invoke-PnCollect.ps1 -SettingsPath tests/fixtures/settings.lab.json -FixturePath tests/fixtures/day1 -Provider m365 -AsOf 2026-11-09T12:00:00Z -OutputPath out/m365Expect the row
m365 gcc 6 5 2 advisory 3, change 1, incident 1, retirement 1: six notices, five open, two needing action.Read the records:
Import-Csv out/m365/notices.csv | Format-Table noticeId, kind, status, actionRequired, actionRequiredBy, titlem365:MC900001is a retirement due2027-01-31T08:00:00Z;m365:MC900003is a change that needs action with no date; the resolved issuem365:TM900102is closed.See who said each one needs action:
(Read-PnJson -Path out/m365/notices.json).notices | ForEach-Object { [pscustomobject]@{ noticeId = $_.noticeId; actionBasis = $_.actionBasis } } | Format-TablenoticeId actionBasis -------- ----------- m365:EX900101 none m365:MC900001 provider: actionRequiredByDateTime m365:MC900002 none m365:MC900003 rule: Message center category preventOrFixIssue m365:MC900004 none m365:TM900102 noneCollect the second week and read what changed:
./Invoke-PnCollect.ps1 -SettingsPath tests/fixtures/settings.lab.json -FixturePath tests/fixtures/day2 -Provider m365 -AsOf 2026-11-16T12:00:00Z -OutputPath out/m365-day2 Import-Csv out/m365-day2/notices.csv | Format-Table noticeId, kind, status, actionRequiredBy, titleSeven notices. A new post,
m365:MC900005, is a change due2027-02-28T08:00:00Z;m365:MC900003’s title now starts(Updated); and the incidentm365:EX900101is closed.Build the live request without sending it, then try a next link on another host:
$s = Read-PnSettings -Path tests/fixtures/settings.lab.json (Get-PnLiveCommand -CallId m365-messages -Settings $s).Uri Get-PnLiveCommand -CallId m365-messages -Settings $s -Arguments @{ nextLink = 'https://graph.example.net/v1.0/admin/serviceAnnouncement/messages?skip=100' }The first line prints
https://graph.microsoft.com/v1.0/admin/serviceAnnouncement/messages. The second is refused:Exception: Refusing https://graph.example.net/v1.0/admin/serviceAnnouncement/messages?skip=100: it isn't on the gcc Graph host https://graph.microsoft.com.Run this chapter’s tests:
Invoke-Pester -Path tests/Collectors.Tests.ps1, tests/Live.Tests.ps1 -FullNameFilter 'Microsoft 365 service communications*', '*Graph*' -Output None -PassThru | Select-Object PassedCount, FailedCountExpect
PassedCount 15,FailedCount 0.
Delete out when you’re done:
Remove-Item -Recurse -Force outExercise: Lab 2, a test tenant (5 minutes to read)
Not run by the authors. Follow the workbook’s Chapter 02, Lab 2. It has the exact commands.
- You need: a Microsoft 365 test tenant in the worldwide or GCC cloud, never production, and a test administrator who holds an admin role; an administrator consents to the two read scopes once.
- What you do: sign in, delegated, with only
ServiceMessage.Read.AllandServiceHealth.Read.All; cross-check Learn’s own cmdlet against the collection; look at the posts that need action; and record the tags you see on retirement posts, which Learn doesn’t list. - Back-out: step 4 signs out, deletes the run, and removes the consent in the Microsoft Entra admin center if it was granted only for this lab.
Knowledge check (5 minutes)
- Which permissions does the Microsoft 365 reader need?
Directory.Read.AllandAuditLog.Read.AllServiceMessage.Read.Allfor posts andServiceHealth.Read.Allfor issues and health overviews- Global Administrator
ServiceHealth.ReadWrite.All
- A GCC tenant is read on which Graph host?
graph.microsoft.usdod-graph.microsoft.usgraph.microsoft.com, the worldwide endpoint- Any of them; the API is the same
m365:MC900003is action required with no date. Why?- Microsoft set
actionRequiredByDateTime - The kit’s rule marks a
preventOrFixIssuepost without a date as action required, and labels itrule: - It is tagged Retirement
- The kit sets every change to action required by default
- Microsoft set
- A response’s
@odata.nextLinkpoints to a host that isn’t the tenant’s Graph host. What does the kit do?- Follows it
- Follows it without the token
- Refuses it
- Stops paging silently
- Who can see data privacy messages in the Message center?
- Any reader with
ServiceMessage.Read.All - Global Administrators and Message Center Privacy Readers
- Nobody through Graph
- Service desk agents
- Any reader with
- b. Taught in What Microsoft gives you.
- c. Taught in the guide’s Step 2 and What Microsoft gives you.
- b. Taught in The code.
- c. Taught in What it leaves to you and the chapter’s Dangers.
- b. Taught in the guide’s prerequisites and What Microsoft gives you.
Summary (2 minutes)
You listed the reader’s two permissions, saw each cloud’s Graph host, collected six posts and issues and then the second week’s seven, told Microsoft’s dates from the kit’s rule, and watched the kit refuse a next link on another host.
The evidence an ISSO keeps for G2 from this module: raw/m365.json, notices.json, notices.csv and manifest.json, dated; the permission list from step 1; and in Lab 2 the consent record. Before shipping, work through the chapter’s Checklist.