Monday, September 14, 2026

Message Center "Action Required By" Notification Automation

Message Center "Action Required By" Notification Automation

Automates alerting on Microsoft 365 admin center Message Center posts that carry an Act by date — the actionRequiredByDateTime field you see in the "Act by" column — and pushes new or updated items to Microsoft Teams and email so nothing with a deadline gets missed in the Message Center inbox.

This fits the "Message Center digest" item already on your AUTOKIT365 backlog.

Assumptions made

Since this is meant to run unattended, a few defaults were picked rather than asked about — change any of them in the runbook parameters or flow before you deploy:

AssumptionDefaultWhere to change
Notify on messages due within14 days-LookaheadDays param
Still show messages that recently became overduelast 30 days-OverdueGraceDays param
Re-notify only when a message is new or its content changedyes (dedupe on lastModifiedDateTime)state cache logic
Run frequencyonce dailyAutomation schedule
Destinationone Teams channel + one mailbox/DLflow inputs

Architecture



The runbook only decides what to notify; the Power Automate flow owns the actual Teams post and email send. This mirrors the split you already use in the app registration / secret expiry monitor (runbook → HTTP-triggered flow → notification).

1. Data source

ItemValue
EndpointGET /admin/serviceAnnouncement/messages (Graph v1.0)
Permission (app, on the managed identity)ServiceMessage.Read.All — admin consent required
Key fieldactionRequiredByDateTime (DateTimeOffset, null when no action is needed)
Other useful fieldstitle, services, category, severity, isMajorChange, lastModifiedDateTime, body.content
Admin center deep link patternhttps://admin.microsoft.com/#/MessageCenter/:/messages/{id}

$filter on actionRequiredByDateTime isn't reliable server-side, so the runbook pulls all current messages and filters locally — same approach as the secret-expiry runbook.

2. Grant the managed identity permission

Run once, as a Global Administrator or Privileged Role Administrator, against the Automation Account's system-assigned managed identity (reuse the same identity as your existing runbooks if they live in the same Automation Account):

Connect-MgGraph -Scopes 'AppRoleAssignment.ReadWrite.All','Application.Read.All'

$graphSp   = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
$identity  = Get-MgServicePrincipal -Filter "displayName eq '<AutomationAccountName>'"
$appRole   = $graphSp.AppRoles | Where-Object { $_.Value -eq 'ServiceMessage.Read.All' -and $_.AllowedMemberTypes -contains 'Application' }

New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $identity.Id `
    -PrincipalId $identity.Id -ResourceId $graphSp.Id -AppRoleId $appRole.Id

3. Deploy the runbook

The full script is Invoke-MessageCenterActionRequiredMonitor.ps1 (delivered alongside this guide). Setup:

StepDetail
Automation AccountReuse your existing M365 Automation Account
RuntimePowerShell 7.x runbook
ModuleMicrosoft.Graph.Authentication
IdentitySystem-assigned managed identity (permission granted in step 2)
Automation variable: MessageCenterWebhookUrlString, encrypted — the Power Automate flow's HTTP POST URL (from step 4)
Automation variable: MessageCenterNotifyStateString, plain — starts empty {}, the runbook maintains it as a dedupe cache
ScheduleDaily (or twice daily if you want tighter latency on new posts)
Dry runRun once manually with -WhatIf to see the payload without posting or updating state

4. Build the Power Automate flow

Create an automated cloud flow with trigger "When a HTTP request is received", then generate its request schema from a sample of the runbook's payload:

{
  "generatedAtUtc": "2026-09-14T08:00:00Z",
  "messages": [
    {
      "id": "MC000000",
      "title": "Example service update",
      "services": "Microsoft Teams",
      "category": "planForChange",
      "severity": "normal",
      "isMajorChange": true,
      "actionRequiredByDate": "2026-10-01",
      "daysRemaining": 17.2,
      "status": "Upcoming",
      "summary": "Plain-text summary of the message body, truncated.",
      "link": "https://admin.microsoft.com/#/MessageCenter/:/messages/MC000000"
    }
  ]
}

Then add:

#ActionConfiguration
1Apply to eachInput: messages from the trigger body
2Teams → Post adaptive card in a chat or channelPost as: Flow bot · Post in: Channel · Team/Channel: your target channel · Adaptive Card: template below
3Outlook → Send an email (V2) (outside the loop, after it)To: your DL/mailbox · Subject: Message Center: action required (@{length(triggerBody()?['messages'])} item(s)) · Body: HTML table built with Create HTML table on messages, set to columns Title/Service/ActbyDate/Status/Link
4ResponseStatus 202, so the runbook's Invoke-RestMethod call returns cleanly

Sending one Teams card per item (step 2) keeps each card actionable and clickable; batching all items into a single daily email (step 3) avoids inbox flooding. Swap that around if you'd rather have one digest card in Teams too — same "Create HTML table" approach can drive a single summary card via a Compose action instead of the loop.

Adaptive Card template (per message)

{
  "type": "AdaptiveCard",
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.4",
  "body": [
    {
      "type": "TextBlock",
      "text": "Message Center: action required",
      "weight": "Bolder",
      "size": "Medium"
    },
    {
      "type": "TextBlock",
      "text": "@{items('Apply_to_each')?['title']}",
      "wrap": true,
      "weight": "Bolder"
    },
    {
      "type": "FactSet",
      "facts": [
        { "title": "Service", "value": "@{items('Apply_to_each')?['services']}" },
        { "title": "Act by", "value": "@{items('Apply_to_each')?['actionRequiredByDate']}" },
        { "title": "Status", "value": "@{items('Apply_to_each')?['status']}" },
        { "title": "Severity", "value": "@{items('Apply_to_each')?['severity']}" }
      ]
    },
    {
      "type": "TextBlock",
      "text": "@{items('Apply_to_each')?['summary']}",
      "wrap": true,
      "isSubtle": true
    }
  ],
  "actions": [
    {
      "type": "Action.OpenUrl",
      "title": "Open in Message Center",
      "url": "@{items('Apply_to_each')?['link']}"
    }
  ]
}

5. Alert logic recap

StatusMeaning
UpcomingAct-by date is more than 7 days out but within the lookahead window
DueSoonAct-by date is within 7 days
OverdueAct-by date has already passed (still surfaced for up to OverdueGraceDays, in case it's still unresolved)

A message is only sent again if it's new, or Microsoft edited it since the last run (lastModifiedDateTime changed) — so a 14-day-out item doesn't re-page you daily as it counts down.

6. Testing checklist

  • Run the runbook manually with -WhatIf — confirm the payload looks right and no HTTP call is made
  • Point MessageCenterWebhookUrl at a test Teams channel first, run once for real, confirm the card renders
  • Confirm the email digest arrives and the HTML table is readable in Outlook
  • Re-run the runbook immediately after a successful run — confirm it reports 0 new items (dedupe working)
  • Manually edit MessageCenterNotifyState back to {} to confirm a full re-notify works as expected
  • Switch the webhook to the real channel/mailbox and enable the schedule

Lighter-weight alternative (no Azure Automation)

If you'd rather not use an Automation Account for this one, the same logic fits in a single Power Automate flow: Recurrence trigger (daily) → HTTP with Microsoft Entra ID action calling GET https://graph.microsoft.com/v1.0/admin/serviceAnnouncement/messages (app registration with ServiceMessage.Read.All, admin consent) → Filter array on actionRequiredByDateTime → the same Teams/email actions as above. You lose the dedupe cache (Power Automate has no simple persistent key-value store without adding Dataverse or SharePoint), so you'd either accept daily re-notifications or add a small SharePoint list to track "already notified" message IDs. The runbook approach above is the one worth building if you want it to match the rest of your AUTOKIT365 monitors.

Note: the built-in "Microsoft 365 message center" connector (Preview) was checked and ruled out for this — its only action is syncing Message Center posts into a Planner plan; it has no Teams or email notification capability.

No comments:

Post a Comment

Featured Post

Message Center "Action Required By" Notification Automation

Message Center "Action Required By" Notification Automation Automates alerting on Microsoft 365 admin center Message Center posts ...

Popular posts