Teams Integration Guide¶
This guide explains how to configure Microsoft Teams notifications for Message Center alerts.
Overview¶
The solution sends adaptive card notifications to a Teams channel when: - A Message Center post has high or critical severity - A post has an action required deadline
Prerequisites¶
- Microsoft Teams channel for alerts
- Power Automate flow with Teams connector
- Adaptive card template (
templates/teams-notification-card.json)
Step 1: Create an Alerts Channel¶
- Open Microsoft Teams
- Navigate to your team (e.g., "Platform Operations")
- Click + Add channel
- Configure:
- Name:
Platform AlertsorMessage Center Alerts - Description: "Automated alerts from M365 Message Center"
- Privacy: Standard (accessible to team members)
- Click Add
Step 2: Connect Power Automate to Teams¶
In your Power Automate flow:
- Add action: Microsoft Teams - Post card in a chat or channel
- Sign in with your Microsoft 365 account
- Configure:
- Post as: Flow bot
- Post in: Channel
- Team: Select your team
- Channel: Select your alerts channel
Action Name Update: Microsoft has renamed several Teams connector actions. The current action is "Post card in a chat or channel". If you have existing flows using "Post adaptive card in a chat or channel", they will continue to work, but new flows should use the current name.
Step 3: Use the Adaptive Card Template¶
The file templates/teams-notification-card.json contains the notification template.
Adaptive Card Version Compatibility¶
Teams supports Adaptive Cards versions 1.0 through 1.5. This solution uses version 1.4, which provides a good balance of features and compatibility. If you need features from version 1.5 (like table elements), update the version in your card JSON.
Option A: Copy-Paste Method¶
- Open
templates/teams-notification-card.json - Copy the entire JSON content
- In Power Automate, paste into the Adaptive Card field
- Replace placeholders with dynamic content (see below)
Option B: Expression Method¶
Build the card dynamically using expressions:
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "@{items('Apply_to_each')?['title']}",
"weight": "Bolder",
"size": "Medium",
"wrap": true
}
]
}
Placeholder Reference¶
| Placeholder | Replace With |
|---|---|
{title} |
@{items('Apply_to_each')?['title']} |
{severity} |
@{items('Apply_to_each')?['severity']} |
{category} |
@{items('Apply_to_each')?['category']} |
{services} |
@{join(coalesce(items('Apply_to_each')?['services'], json('[]')), ', ')} |
{startDateTime} |
@{formatDateTime(items('Apply_to_each')?['startDateTime'], 'MMM dd, yyyy')} |
{actionRequiredByDateTime} |
@{if(equals(items('Apply_to_each')?['actionRequiredByDateTime'], null), 'None', formatDateTime(items('Apply_to_each')?['actionRequiredByDateTime'], 'MMM dd, yyyy'))} |
{id} |
@{items('Apply_to_each')?['id']} |
{recordId} |
@{outputs('Upsert_a_row')?['body/fsi_messagecenterlogid']} — ) |
Step 4: Configure Notification Conditions¶
Only send notifications for important posts. In your flow:
- Add a Condition action before the Teams action
- Configure the condition:
High Severity OR Action Required (with duplicate prevention):
@and(
equals(outputs('Upsert_a_row')?['body/fsi_notifiedon'], null),
or(
equals(items('Apply_to_each')?['severity'], 'high'),
equals(items('Apply_to_each')?['severity'], 'critical'),
not(equals(items('Apply_to_each')?['actionRequiredByDateTime'], null))
)
)
Note: The
notifiedOncheck uses the Dataverse upsert response (not the Graph API message) to prevent re-notifying posts that were already sent to Teams. See Flow Configuration Step 7 for full details and alternative expressions.
Or use the visual editor:
- Condition 1: @{outputs('Upsert_a_row')?['body/fsi_notifiedon']} is equal to null
- AND (click "Add group" → OR group for the conditions below):
- Condition 2: severity equals high
- OR
- Condition 3: severity equals critical
- OR
- Condition 4: actionRequiredByDateTime is not equal to null
Grouping matters: You must nest Conditions 2–4 inside an OR group, then AND that group with Condition 1 (the null check). Without explicit grouping, the default evaluation would be
(notifiedOn == null AND severity == 'high') OR severity == 'critical' OR actionRequired != null, which bypasses duplicate prevention for critical-severity and action-required posts. See Flow Configuration Step 7 for full details.
Step 5: Add User Mentions (Optional)¶
To @mention specific users for urgent posts:
- In the adaptive card, add a mention entity:
{
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "<at>Platform Team</at> - Action Required!",
"wrap": true
}
],
"msteams": {
"entities": [
{
"type": "mention",
"text": "<at>Platform Team</at>",
"mentioned": {
"id": "your-team-id-or-user-id",
"name": "Platform Team"
}
}
]
}
}
Getting User/Team IDs¶
For users:
- Use Microsoft Graph: GET /users/{user-principal-name}
- The id field is the user's GUID
For teams:
- Use Microsoft Graph: GET /groups?$filter=resourceProvisioningOptions/Any(x:x eq 'Team')
- The id field is the team's GUID
Card Design Options¶
Minimal Card¶
Shows just the essentials:
{
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "{title}",
"weight": "Bolder",
"wrap": true
},
{
"type": "TextBlock",
"text": "Severity: {severity}",
"spacing": "Small"
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View in Admin Center",
"url": "https://admin.microsoft.com/Adminportal/Home#/MessageCenter/:/messages/{id}"
}
]
}
Full Card (Default)¶
See templates/teams-notification-card.json for the complete template with:
- Color-coded severity indicator
- All key metadata
- Services affected
- Action deadline (if any)
- Direct link to Admin Center
Notification Routing¶
By Severity¶
Route different severity levels to different channels:
High Severity → #platform-alerts-urgent (with @mentions)
Normal Severity → #platform-alerts (no notification)
By Service¶
Route by affected service:
Implementation:
Use a Switch action based on the services array, or multiple conditions.
Best Practices¶
Don't Over-Notify¶
- Only alert on high severity and action-required posts
- Most Message Center posts are informational (normal severity)
- Too many notifications = notification fatigue
Use Actionable Cards¶
- Include a direct link to the Admin Center post
- Include a link to your Dataverse record (for assessment) - see Dataverse Record Link below
- Consider adding quick actions (e.g., "Mark as Reviewed")
Dataverse Record Link (Optional)¶
The adaptive card template includes an "Assess Record" button that links directly to the Dataverse record. This enables faster triage by letting your team jump straight to the assessment form.
Prerequisites¶
To use this feature, you need: - A model-driven app or canvas app that displays MessageCenterLog records - The Dataverse row GUID from the upsert operation in your flow
Getting the Record ID¶
After upserting to Dataverse, the response includes the row GUID:
- In your flow, after the Dataverse upsert action, add a Compose action
- Set the input to:
@{outputs('Upsert_a_row')?['body/fsi_messagecenterlogid']} - Use this value for
{recordId}in the adaptive card URL
URL Format¶
The Dataverse record URL follows this pattern:
https://[your-environment].crm.dynamics.com/main.aspx?appid=[app-id]&pagetype=entityrecord&etn=[table-logical-name]&id=[record-guid]
Components:
| Component | Description | Example |
|---|---|---|
[your-environment] |
Your Dataverse environment name | contoso |
[app-id] |
GUID of your model-driven app | 12345678-1234-1234-1234-123456789abc |
[table-logical-name] |
Logical name of MessageCenterLog table | fsi_messagecenterlog |
[record-guid] |
Row GUID from upsert response | Dynamic from flow |
Finding Your App ID¶
- Open Power Apps (make.powerapps.com)
- Navigate to your model-driven app
- Click ... > Details
- Copy the App ID (GUID)
Or use the URL when the app is open - the appid parameter is in the URL.
Example Card Configuration¶
In your Power Automate flow, replace the placeholder URL:
{
"type": "Action.OpenUrl",
"title": "Assess Record",
"url": "https://contoso.crm.dynamics.com/main.aspx?appid=12345678-1234-1234-1234-123456789abc&pagetype=entityrecord&etn=[publisher-prefix]_messagecenterlog&id=@{outputs('Upsert_a_row')?['body/[publisher-prefix]_messagecenterlogid']}"
}
Note: If you don't have a model-driven app deployed, you can remove this action from the card template or leave it as a placeholder for future use.
Finding Your Publisher Prefix¶
Dataverse column logical names include a publisher prefix (e.g., fsi_messagecenterlogid). To find your prefix:
Method 1: Via Power Apps Tables
- Go to make.powerapps.com
- Select your environment (top right)
- Navigate to Tables > MessageCenterLog
- Click on any custom column (e.g.,
messagecenterid) - In the column details panel, find Logical name
- The prefix is everything before the underscore (e.g.,
fsi_infsi_messagecenterid)
Method 2: Via Solution Publisher
- Go to make.powerapps.com > Solutions
- Open the solution containing your table
- Click Settings (gear icon) > Publishers
- Find your publisher and note the Prefix value
Method 3: Via Dataverse Upsert Response
- Run your flow with the Dataverse upsert action
- Check the flow run history
- Look at the upsert action output—the returned field names show the prefix
Common prefixes: Default environments often use
cr...prefixes (e.g.,fsi_). Custom publishers use the prefix you specified when creating the publisher.
Monitor Flow Health¶
- Set up a separate alert if the flow fails
- Check flow run history weekly
- Ensure the client secret doesn't expire unnoticed
Troubleshooting¶
Card Not Displaying¶
- Validate JSON at adaptivecards.io/designer
- Ensure version is "1.4" or "1.5" (Teams supports versions 1.0-1.5)
- Check for unsupported features in Teams (some Adaptive Card features are desktop-only)
Mentions Not Working¶
- Verify the user/team ID is correct
- The
textfield must match exactly (e.g.,<at>Name</at>) - User must be a member of the channel
Notifications Not Appearing¶
- Verify flow ran successfully (check run history)
- Confirm channel ID is correct
- Check Teams connector permissions
- Ensure the bot has permission to post to the channel
Rate Limiting¶
Teams has rate limits for incoming messages: - Per-channel: 1 message per second - Per-app: 50 messages per minute
Daily polling is well within these limits.