|
| 1 | +# Teams Workflows |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The Teams Workflows notification service sends message notifications using Microsoft Teams Workflows (Power Automate). This is the recommended replacement for the legacy Office 365 Connectors service, which will be retired on March 31, 2026. |
| 6 | + |
| 7 | +## Parameters |
| 8 | + |
| 9 | +The Teams Workflows notification service requires specifying the following settings: |
| 10 | + |
| 11 | +* `recipientUrls` - the webhook url map, e.g. `channelName: https://api.powerautomate.com/webhook/...` |
| 12 | + |
| 13 | +## Supported Webhook URL Formats |
| 14 | + |
| 15 | +The service supports the following Microsoft Teams Workflows webhook URL patterns: |
| 16 | + |
| 17 | +- `https://api.powerautomate.com/...` |
| 18 | +- `https://api.powerplatform.com/...` |
| 19 | +- `https://flow.microsoft.com/...` |
| 20 | +- URLs containing `/powerautomate/` in the path |
| 21 | + |
| 22 | +## Configuration |
| 23 | + |
| 24 | +1. Open `Teams` and go to the channel you wish to set notifications for |
| 25 | +2. Click on the 3 dots next to the channel name |
| 26 | +3. Select`Workflows` |
| 27 | +4. Click on `Manage` |
| 28 | +5. Click `New flow` |
| 29 | +6. Write `Send webhook alerts to a channel` in the search bar or select it from the template list |
| 30 | +7. Choose your team and channel |
| 31 | +8. Configure the webhook name and settings |
| 32 | +9. Copy the webhook URL (it will be from `api.powerautomate.com`, `api.powerplatform.com`, or `flow.microsoft.com`) |
| 33 | +10. Store it in `argocd-notifications-secret` and define it in `argocd-notifications-cm` |
| 34 | + |
| 35 | +```yaml |
| 36 | +apiVersion: v1 |
| 37 | +kind: ConfigMap |
| 38 | +metadata: |
| 39 | + name: argocd-notifications-cm |
| 40 | +data: |
| 41 | + service.teams-workflows: | |
| 42 | + recipientUrls: |
| 43 | + channelName: $channel-workflows-url |
| 44 | +``` |
| 45 | +
|
| 46 | +```yaml |
| 47 | +apiVersion: v1 |
| 48 | +kind: Secret |
| 49 | +metadata: |
| 50 | + name: <secret-name> |
| 51 | +stringData: |
| 52 | + channel-workflows-url: https://api.powerautomate.com/webhook/your-webhook-id |
| 53 | +``` |
| 54 | +
|
| 55 | +11. Create subscription for your Teams Workflows integration: |
| 56 | +
|
| 57 | +```yaml |
| 58 | +apiVersion: argoproj.io/v1alpha1 |
| 59 | +kind: Application |
| 60 | +metadata: |
| 61 | + annotations: |
| 62 | + notifications.argoproj.io/subscribe.on-sync-succeeded.teams-workflows: channelName |
| 63 | +``` |
| 64 | +
|
| 65 | +## Channel Support |
| 66 | +
|
| 67 | +- ✅ Standard Teams channels |
| 68 | +- ✅ Shared channels (as of December 2025) |
| 69 | +- ✅ Private channels (as of December 2025) |
| 70 | +
|
| 71 | +Teams Workflows provides enhanced channel support compared to Office 365 Connectors, allowing you to post to shared and private channels in addition to standard channels. |
| 72 | +
|
| 73 | +## Adaptive Card Format |
| 74 | +
|
| 75 | +The Teams Workflows service uses **Adaptive Cards** exclusively, which is the modern, flexible card format for Microsoft Teams. All notifications are automatically converted to Adaptive Card format and wrapped in the required message envelope. |
| 76 | +
|
| 77 | +### Option 1: Using Template Fields (Recommended) |
| 78 | +
|
| 79 | +The service automatically converts template fields to Adaptive Card format. This is the simplest and most maintainable approach: |
| 80 | +
|
| 81 | +```yaml |
| 82 | +template.app-sync-succeeded: | |
| 83 | + teams-workflows: |
| 84 | + # ThemeColor supports Adaptive Card semantic colors: "Good", "Warning", "Attention", "Accent" |
| 85 | + # or hex colors like "#000080" |
| 86 | + themeColor: "Good" |
| 87 | + title: Application {{.app.metadata.name}} has been successfully synced |
| 88 | + text: Application {{.app.metadata.name}} has been successfully synced at {{.app.status.operationState.finishedAt}}. |
| 89 | + summary: "{{.app.metadata.name}} sync succeeded" |
| 90 | + facts: | |
| 91 | + [{ |
| 92 | + "name": "Sync Status", |
| 93 | + "value": "{{.app.status.sync.status}}" |
| 94 | + }, { |
| 95 | + "name": "Repository", |
| 96 | + "value": "{{.app.spec.source.repoURL}}" |
| 97 | + }] |
| 98 | + sections: | |
| 99 | + [{ |
| 100 | + "facts": [ |
| 101 | + { |
| 102 | + "name": "Namespace", |
| 103 | + "value": "{{.app.metadata.namespace}}" |
| 104 | + }, |
| 105 | + { |
| 106 | + "name": "Cluster", |
| 107 | + "value": "{{.app.spec.destination.server}}" |
| 108 | + } |
| 109 | + ] |
| 110 | + }] |
| 111 | + potentialAction: |- |
| 112 | + [{ |
| 113 | + "@type": "OpenUri", |
| 114 | + "name": "View in Argo CD", |
| 115 | + "targets": [{ |
| 116 | + "os": "default", |
| 117 | + "uri": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}" |
| 118 | + }] |
| 119 | + }] |
| 120 | +``` |
| 121 | +
|
| 122 | +**How it works:** |
| 123 | +- `title` → Converted to a large, bold TextBlock |
| 124 | +- `text` → Converted to a regular TextBlock |
| 125 | +- `facts` → Converted to a FactSet element |
| 126 | +- `sections` → Facts within sections are extracted and converted to FactSet elements |
| 127 | +- `potentialAction` → OpenUri actions are converted to Action.OpenUrl |
| 128 | +- `themeColor` → Applied to the title TextBlock (supports semantic colors like "Good", "Warning", "Attention", "Accent" or hex colors) |
| 129 | + |
| 130 | +### Option 2: Custom Adaptive Card JSON |
| 131 | + |
| 132 | +For full control and advanced features, you can provide a complete Adaptive Card JSON template: |
| 133 | + |
| 134 | +```yaml |
| 135 | +template.app-sync-succeeded: | |
| 136 | + teams-workflows: |
| 137 | + adaptiveCard: | |
| 138 | + { |
| 139 | + "type": "AdaptiveCard", |
| 140 | + "version": "1.4", |
| 141 | + "body": [ |
| 142 | + { |
| 143 | + "type": "TextBlock", |
| 144 | + "text": "Application {{.app.metadata.name}} synced successfully", |
| 145 | + "size": "Large", |
| 146 | + "weight": "Bolder", |
| 147 | + "color": "Good" |
| 148 | + }, |
| 149 | + { |
| 150 | + "type": "TextBlock", |
| 151 | + "text": "Application {{.app.metadata.name}} has been successfully synced at {{.app.status.operationState.finishedAt}}.", |
| 152 | + "wrap": true |
| 153 | + }, |
| 154 | + { |
| 155 | + "type": "FactSet", |
| 156 | + "facts": [ |
| 157 | + { |
| 158 | + "title": "Sync Status", |
| 159 | + "value": "{{.app.status.sync.status}}" |
| 160 | + }, |
| 161 | + { |
| 162 | + "title": "Repository", |
| 163 | + "value": "{{.app.spec.source.repoURL}}" |
| 164 | + } |
| 165 | + ] |
| 166 | + } |
| 167 | + ], |
| 168 | + "actions": [ |
| 169 | + { |
| 170 | + "type": "Action.OpenUrl", |
| 171 | + "title": "View in Argo CD", |
| 172 | + "url": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}" |
| 173 | + } |
| 174 | + ] |
| 175 | + } |
| 176 | +``` |
| 177 | + |
| 178 | +**Note:** When using `adaptiveCard`, you only need to provide the AdaptiveCard JSON structure (not the full message envelope). The service automatically wraps it in the required `message` + `attachments` format for Teams Workflows. |
| 179 | + |
| 180 | +**Important:** If you provide `adaptiveCard`, it takes precedence over all other template fields (`title`, `text`, `facts`, etc.). |
| 181 | + |
| 182 | +## Template Fields |
| 183 | + |
| 184 | +The Teams Workflows service supports the following template fields, which are automatically converted to Adaptive Card format: |
| 185 | + |
| 186 | +### Standard Fields |
| 187 | + |
| 188 | +- `title` - Message title (converted to large, bold TextBlock) |
| 189 | +- `text` - Message text content (converted to TextBlock) |
| 190 | +- `summary` - Summary text (currently not used in Adaptive Cards, but preserved for compatibility) |
| 191 | +- `themeColor` - Color for the title. Supports: |
| 192 | + - Semantic colors: `"Good"` (green), `"Warning"` (yellow), `"Attention"` (red), `"Accent"` (blue) |
| 193 | + - Hex colors: `"#000080"`, `"#FF0000"`, etc. |
| 194 | +- `facts` - JSON array of fact key-value pairs (converted to FactSet) |
| 195 | + ```yaml |
| 196 | + facts: | |
| 197 | + [{ |
| 198 | + "name": "Status", |
| 199 | + "value": "{{.app.status.sync.status}}" |
| 200 | + }] |
| 201 | + ``` |
| 202 | +- `sections` - JSON array of sections containing facts (facts are extracted and converted to FactSet) |
| 203 | + ```yaml |
| 204 | + sections: | |
| 205 | + [{ |
| 206 | + "facts": [{ |
| 207 | + "name": "Namespace", |
| 208 | + "value": "{{.app.metadata.namespace}}" |
| 209 | + }] |
| 210 | + }] |
| 211 | + ``` |
| 212 | +- `potentialAction` - JSON array of action buttons (OpenUri actions converted to Action.OpenUrl) |
| 213 | + ```yaml |
| 214 | + potentialAction: |- |
| 215 | + [{ |
| 216 | + "@type": "OpenUri", |
| 217 | + "name": "View Details", |
| 218 | + "targets": [{ |
| 219 | + "os": "default", |
| 220 | + "uri": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}" |
| 221 | + }] |
| 222 | + }] |
| 223 | + ``` |
| 224 | + |
| 225 | +### Advanced Fields |
| 226 | + |
| 227 | +- `adaptiveCard` - Complete Adaptive Card JSON template (takes precedence over all other fields) |
| 228 | + - Only provide the AdaptiveCard structure, not the message envelope |
| 229 | + - Supports full Adaptive Card 1.4 specification |
| 230 | + - Allows access to all Adaptive Card features (containers, columns, images, etc.) |
| 231 | + |
| 232 | +- `template` - Raw JSON template (legacy, use `adaptiveCard` instead) |
| 233 | + |
| 234 | +### Field Conversion Details |
| 235 | + |
| 236 | +| Template Field | Adaptive Card Element | Notes | |
| 237 | +|---------------|----------------------|-------| |
| 238 | +| `title` | `TextBlock` with `size: "Large"`, `weight: "Bolder"` | ThemeColor applied to this element | |
| 239 | +| `text` | `TextBlock` with `wrap: true` | Uses `n.Message` if `text` is empty | |
| 240 | +| `facts` | `FactSet` | Each fact becomes a `title`/`value` pair | |
| 241 | +| `sections[].facts` | `FactSet` | Facts extracted from sections | |
| 242 | +| `potentialAction[OpenUri]` | `Action.OpenUrl` | Only OpenUri actions are converted | |
| 243 | +| `themeColor` | Applied to title `TextBlock.color` | Supports semantic and hex colors | |
| 244 | + |
| 245 | +## Migration from Office 365 Connectors |
| 246 | + |
| 247 | +If you're currently using the `teams` service with Office 365 Connectors, follow these steps to migrate: |
| 248 | + |
| 249 | +1. **Create a new Workflows webhook** using the configuration steps above |
| 250 | + |
| 251 | +2. **Update your service configuration:** |
| 252 | + - Change from `service.teams` to `service.teams-workflows` |
| 253 | + - Update the webhook URL to your new Workflows webhook URL |
| 254 | + |
| 255 | +3. **Update your templates:** |
| 256 | + - Change `teams:` to `teams-workflows:` in your templates |
| 257 | + - Your existing template fields (`title`, `text`, `facts`, `sections`, `potentialAction`) will automatically be converted to Adaptive Card format |
| 258 | + - No changes needed to your template structure - the conversion is automatic |
| 259 | + |
| 260 | +4. **Update your subscriptions:** |
| 261 | + ```yaml |
| 262 | + # Old |
| 263 | + notifications.argoproj.io/subscribe.on-sync-succeeded.teams: channelName |
| 264 | + |
| 265 | + # New |
| 266 | + notifications.argoproj.io/subscribe.on-sync-succeeded.teams-workflows: channelName |
| 267 | + ``` |
| 268 | + |
| 269 | +5. **Test and verify:** |
| 270 | + - Send a test notification to verify it works correctly |
| 271 | + - Once verified, you can remove the old Office 365 Connector configuration |
| 272 | + |
| 273 | +**Note:** Your existing templates will work without modification. The service automatically converts your template fields to Adaptive Card format, so you get the benefits of modern cards without changing your templates. |
| 274 | + |
| 275 | +## Differences from Office 365 Connectors |
| 276 | + |
| 277 | +| Feature | Office 365 Connectors | Teams Workflows | |
| 278 | +|---------|----------------------|-----------------| |
| 279 | +| Service Name | `teams` | `teams-workflows` | |
| 280 | +| Standard Channels | ✅ | ✅ | |
| 281 | +| Shared Channels | ❌ | ✅ (Dec 2025+) | |
| 282 | +| Private Channels | ❌ | ✅ (Dec 2025+) | |
| 283 | +| Card Format | messageCard (legacy) | Adaptive Cards (modern) | |
| 284 | +| Template Conversion | N/A | Automatic conversion from template fields | |
| 285 | +| Retirement Date | March 31, 2026 | Active | |
| 286 | + |
| 287 | +## Adaptive Card Features |
| 288 | + |
| 289 | +The Teams Workflows service leverages Adaptive Cards, which provide: |
| 290 | + |
| 291 | +- **Rich Content**: Support for text, images, fact sets, and more |
| 292 | +- **Flexible Layout**: Containers, columns, and adaptive layouts |
| 293 | +- **Interactive Elements**: Action buttons, input fields, and more |
| 294 | +- **Semantic Colors**: Built-in color schemes (Good, Warning, Attention, Accent) |
| 295 | +- **Cross-Platform**: Works across Teams, Outlook, and other Microsoft 365 apps |
| 296 | + |
| 297 | +### Example: Advanced Adaptive Card Template |
| 298 | + |
| 299 | +For complex notifications, you can use the full Adaptive Card specification: |
| 300 | + |
| 301 | +```yaml |
| 302 | +template.app-sync-succeeded-advanced: | |
| 303 | + teams-workflows: |
| 304 | + adaptiveCard: | |
| 305 | + { |
| 306 | + "type": "AdaptiveCard", |
| 307 | + "version": "1.4", |
| 308 | + "body": [ |
| 309 | + { |
| 310 | + "type": "Container", |
| 311 | + "items": [ |
| 312 | + { |
| 313 | + "type": "ColumnSet", |
| 314 | + "columns": [ |
| 315 | + { |
| 316 | + "type": "Column", |
| 317 | + "width": "auto", |
| 318 | + "items": [ |
| 319 | + { |
| 320 | + "type": "Image", |
| 321 | + "url": "https://example.com/success-icon.png", |
| 322 | + "size": "Small" |
| 323 | + } |
| 324 | + ] |
| 325 | + }, |
| 326 | + { |
| 327 | + "type": "Column", |
| 328 | + "width": "stretch", |
| 329 | + "items": [ |
| 330 | + { |
| 331 | + "type": "TextBlock", |
| 332 | + "text": "Application {{.app.metadata.name}}", |
| 333 | + "weight": "Bolder", |
| 334 | + "size": "Large" |
| 335 | + }, |
| 336 | + { |
| 337 | + "type": "TextBlock", |
| 338 | + "text": "Successfully synced", |
| 339 | + "spacing": "None", |
| 340 | + "isSubtle": true |
| 341 | + } |
| 342 | + ] |
| 343 | + } |
| 344 | + ] |
| 345 | + }, |
| 346 | + { |
| 347 | + "type": "FactSet", |
| 348 | + "facts": [ |
| 349 | + { |
| 350 | + "title": "Status", |
| 351 | + "value": "{{.app.status.sync.status}}" |
| 352 | + }, |
| 353 | + { |
| 354 | + "title": "Repository", |
| 355 | + "value": "{{.app.spec.source.repoURL}}" |
| 356 | + } |
| 357 | + ] |
| 358 | + } |
| 359 | + ] |
| 360 | + } |
| 361 | + ], |
| 362 | + "actions": [ |
| 363 | + { |
| 364 | + "type": "Action.OpenUrl", |
| 365 | + "title": "View in Argo CD", |
| 366 | + "url": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}" |
| 367 | + } |
| 368 | + ] |
| 369 | + } |
| 370 | +``` |
0 commit comments