MS Teams is retiring webhooks - Power Automate workflows is the new black
Current Behavior
M$ is retiring the classic webhooks and you'll have to use Power Automate workflows instead.
The linked page doesn't have too many details about the transition so I thought it'd be a swap & replace. But apparently that's not true. With the existing template my workflow errs with:
... "Send_each_adaptive_card": The execution of template action 'Send_each_adaptive_card' failed: the result of the evaluation of 'foreach' expression '@triggerOutputs()?['body']?['attachments']' is of type 'Null'. The result must be a valid array.
Proposed Behavior
I guess it'd be a good idea to have a new template for notifications for workflows that works out of the box and has an adequate name.
Checklist
- [X] I have read and understand the contributing guidelines
- [X] I have checked the existing issues for whether this enhancement was already requested
Quick headsup because I tried it this morning as well with dependency track.
Its not enough to wrap the existing message into the new payload structure that comes with the default template for sending messages to a teams channel (see)
They also only accept adaptive cards there, no more MessageCards or even the simpler text version of the old webhook. If you however put the example from the link above into a custom template in DTrack it works. So I guess we need a new adaptive card template.
Thanks for looking into this @otbe. Did you just reuse org.dependencytrack.notification.publisher.MsTeamsPublisher for your custom template?
Thanks for looking into this @otbe. Did you just reuse
org.dependencytrack.notification.publisher.MsTeamsPublisherfor your custom template?
Yes exactly.
I'll be giving this a shot:
{
"type":"message",
"attachments":[
{
"contentType":"application/vnd.microsoft.card.adaptive",
"contentUrl":"{{ baseUrl }}/projects/{{ subject.project.uuid | escape(strategy='json') }}",
"content":{
"$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
"type":"AdaptiveCard",
"version":"1.2",
"body":[
{
"type": "TextBlock",
{% if notification.group == "POLICY_VIOLATION" %}
"text": "{{ subject.policyViolation.policyCondition.subject | escape(strategy="json") }} {{ subject.component.toString | escape(strategy="json") }} - {{ notification.content | escape(strategy="json") }}"
{% elseif notification.group == "BOM_PROCESSING_FAILED" %}
"text": "BOM processing failed for {{ subject.project.toString | escape(strategy="json") }} - {{ notification.content | escape(strategy="json") }}"
{% else %}
"text": "{{ notification.content | escape(strategy="json") }}"
{% endif %}
}
]
}
}
]
}
Should be easy to add a new template then. Perhaps I'll do it today or tomorrow.
Hi, i'm suffering from the same problem. Unfortunately i'm not too deep into this subjects and i have to admit, that i have not understood everything from your post. I managed to create a workflow 'post to channel...'. When i post your templete to the workflow url, a message is displayed in the teams channel, but it's empty. No text at all. May i kindly ask, to have a look? Is there something missing in my payload?
4 def send_teams_message(flow_url, message):
5 headers = {
6 "Content-Type": "application/json"
7 }
8
9 proxyip = "blinded"
10 proxyport = "3128"
11 proxies= {"https": f"http://{proxyip}:{proxyport}"}
12
13 #payload = {
14 # "message": message
15 #}
16 payload = {
17 "title": "Ueberschrift",
18 "type":"AdaptiveCard",
19 "attachments":[
20 {
21 "contentType":"application/vnd.microsoft.card.adaptive",
22 "contentUrl":None,
23 "content":{
24 "$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
25 "type":"AdaptiveCard",
26 "version":"1.2",
27 "body":[
28 {
29 "type": "TextBlock",
30 "size": "Medium",
31 "weight": "Bolder",
32 "text": "Bla Bla"
33 }
34 ]
35 }
36 }
37 ]
38 }
39
40
41 response = requests.post(flow_url, headers=headers, json=payload, proxies=proxies)
42
43 if response.status_code == 202:
44 print("Message sent successfully")
45 else:
46 print(f"Failed to send message. Status code: {response.status_code}")
47 print(f"Response: {response.text}")
Works - so all there is to do (probs) is deciding what to put where to have the information you need and to make it look pretty.
@hajohoetger didn't go through your code but it looks different from the template "draft" I posted above. Give it a try.
P.S.: You can use triple backticks to get a code block or > for quotes - makes it way easier to read.
I got this far, it works for new vulnerabilities.
{ "attachments": [ { "contentType": "object", "content": { "type": "AdaptiveCard", "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.2", "body": [ { "type": "TextBlock", "size": "Medium", "weight": "Bolder", "text": "{{ notification.title | escape(strategy="json") }}" }, { "type": "TextBlock", "text": "Dependency-Track", "weight": "Bolder", "spacing": "Medium" }, { "type": "TextBlock", "text": "{{ timestamp }}", "spacing": "None" }, { "type": "Image", "url": "https://raw.githubusercontent.com/DependencyTrack/branding/master/dt-logo-symbol-blue-background.png", "size": "Small", "spacing": "Medium" }, { "type": "FactSet", "facts": [ { "title": "VulnID", "value":"{{ subject.vulnerability.vulnId | escape(strategy="json") }}" }, { "title": "Severity", "value": "{{ subject.vulnerability.severity | escape(strategy="json") }}" }, { "title": "Source", "value": "{{ subject.vulnerability.source | escape(strategy="json") }}" }, { "title": "Component", "value": "{{ subject.component.toString | escape(strategy="json") }}" } ] }, { "type": "TextBlock", "text": "{{ notification.content | escape(strategy="json") }}", "wrap": true } ] } } ] }
Use the teams workflow as below:
Hi, sorry to bother you again, but i'm trying for hours now without success. I did manage to display my card in teams, but it is cut, though i have defined "targetWidth": "Wide". I tried that in all parts but that does not change the layout. I just want the card to fit the width of the screen. Often the expert sees the problem at first sight. Then it would be very nice to hint me on that...
proxies= {
"https": f"http://{proxyip}:{proxyport}"
}
payload = {
"type":"AdaptiveCard",
"attachments":[
{
"contentType":"application/vnd.microsoft.card.adaptive",
"contentUrl":None,
"content":{
"$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
"type":"AdaptiveCard",
"version":"1.2",
"targetWidth": "Wide",
"body":[
{
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": "Provisionierung erfolgt!"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "Ressource:"
},
{
"type": "TextBlock",
"text": "UserID:"
},
{
"type": "TextBlock",
"text": "Action:"
}
],
"width": "auto"
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": ressource
},
{
"type": "TextBlock",
"text": uid
},
{
"type": "TextBlock",
"text": action
}
],
"width": "stretch"
}
]
}
]
}
}
]
}
response = requests.post(url[stage], headers=headers, json=payload, proxies=proxies)
The output in Teams looks like this:
I used three ticks to make the code more readeable, but that didn't work. Maybe, if i upload that as file:
@hajohoetger I used three ticks to make the code more readeable, but that didn't work.
It has to be backticks: ```
@hajohoetger I used three ticks to make the code more readeable, but that didn't work.
It has to be backticks:
```
...indeed, that works! (Have edited my former posts.) Thank you! :-)
Getting below error while posting a message with using new workflow URL
The execution of template action 'Send_each_adaptive_card' failed: the result of the evaluation of 'foreach' expression '@triggerOutputs()?['body']?['attachments']' is of type 'Null'. The result must be a valid array.
I am using legacy card like that used to work with webhook URL. It's throwing an above error when use workflow URL
[Array]$audit =
@{
name = "LastBuildStatus and Provisioning State"
value = ("$($results.LastRunStatusRunState)" + " - " + "$($results.ProvisioningState -join ',')")
}
$body = ConvertTo-Json -Depth 10 @{
title = "Status"
text = "Reminder "
separator = "true"
sections = @(
@{
activityTitle = "Test "
activitySubtitle = "Please take an action, accordingly, refer [document](https: doc link)"
facts = $audit
}
)
}
Invoke-RestMethod -Method post -ContentType 'application/Json' -Body $body -Uri $workflowUrl
Any one encountered same issue? Do we really need to convert legacy card into adaptative card format ?
Yes we do. Apparently they realized the time window was a bit short and they are going to support old webhooks for longer but you still can no longer create them, only power automate workflows. And they expect a different body with attachments of adaptive cards. I haven't come around to file a PR as the duct taped version above is GEFN for me 🙃
Here is an adaptive-card-template that works well for me:
{
"type":"message",
"attachments":[
{
"contentType":"application/vnd.microsoft.card.adaptive",
"contentUrl":"{{ baseUrl }}/projects/{{ subject.project.uuid | escape(strategy='json') }}",
"content":{
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": "{{ notification.title | escape(strategy="json") }}",
"wrap": true
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "Image",
"style": "Person",
"url": "https://raw.githubusercontent.com/DependencyTrack/branding/master/dt-logo-symbol-blue-background.png",
"altText": "DependencyTrack",
"size": "Small"
}
],
"width": "auto"
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "DependencyTrack",
"wrap": true
},
{
"type": "TextBlock",
"spacing": "None",
"text": "{{ timestamp }}",
"isSubtle": true,
"wrap": true
}
],
"width": "stretch"
}
]
},
{
"type": "TextBlock",
"text": "{{ notification.content | escape(strategy="json") }}",
"wrap": true
},
{% if notification.group == "NEW_VULNERABILITY" %}
{
"type": "FactSet",
"facts": [
{
"title": "Project:",
"value": "{{ subject.component.project.toString | escape(strategy="json") }}"
},
{
"title": "Component:",
"value": "{{ subject.component.toString | escape(strategy="json") }}"
},
{
"title": "Version:",
"value": "{{ subject.component.version | escape(strategy="json") }}"
},
{
"title": "Severity:",
"value": "{{ subject.vulnerability.severity | escape(strategy="json") }}"
},
{
"title": "VulnId:",
"value": "{{ subject.vulnerability.vulnId | escape(strategy="json") }}"
}
]
}
{% elseif notification.group == "NEW_VULNERABLE_DEPENDENCY" %}
{
"type": "FactSet",
"facts": [
{
"title": "Project:",
"value": "{{ subject.component.project.toString | escape(strategy="json") }}"
},
{
"title": "Component:",
"value": "{{ subject.component.toString | escape(strategy="json") }}"
},
{
"title": "Version:",
"value": "{{ subject.component.version | escape(strategy="json") }}"
}
]
}
{% elseif notification.group == "POLICY_VIOLATION" %}
{
"type": "FactSet",
"facts": [
{
"title": "Project:",
"value": "{{ subject.component.project.toString | escape(strategy="json") }}"
},
{
"title": "Component:",
"value": "{{ subject.component.toString | escape(strategy="json") }}"
},
{
"title": "Version:",
"value": "{{ subject.component.version | escape(strategy="json") }}"
}
]
}
{% elseif notification.group == "BOM_PROCESSING_FAILED" %}
{
"type": "FactSet",
"facts": [
{
"title": "Project:",
"value": "{{ subject.component.project.toString | escape(strategy="json") }}"
},
{
"title": "Component:",
"value": "{{ subject.component.toString | escape(strategy="json") }}"
},
{
"title": "Version:",
"value": "{{ subject.component.version | escape(strategy="json") }}"
}
]
}
{% endif %}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4"
}
}
]
}
Anything on this that could be included per default in DT? Do we need two separate publishers? One for the legacy, and one for the new format?
Someone still has to sit down and work this out for the general case.
As the legacy connectors' life span was extended we should have both.
@black-snow My template works for the general case.
There is a designer available here to work on the template: https://adaptivecards.io/
Someone still has to sit down and actually file a PR with @fabian-zeindl-oebb 's template :D
Note that Microsoft has updated the guidance in the original post and now says that they are looking into allowing messages to continue to be posted using the MessageCard format:
For those like me who read the above message and were hoping to have an update, we'll have to wait til August:
Update 04/11/2025: New information related to Office 365 Connectors Retirement in Teams – Webhook URL Migration We are still developing a method for webhooks in the Workflow app to support the following scenarios and will share more details before the end of August 2025.
- Post messageCard formatted message payloads (so you do not have to reformat the payload to adaptive card)
- Post to private channels
Big thanks to @fabian-zeindl-oebb for the template!
I've tweaked it slightly, replacing the component.toString with component.name so the component version doesn't appear twice :)
https://gist.github.com/binaryoverload/a87d243a41eab0749f64adfea964d1ff
This is the template we use: https://gist.github.com/elmuerte/54fc63ccc07ed82b28ac9c573b2b26e9
Besides generalizing some parts in the notification it offers a bunch of deeplinks where possible. It also contains a filter at the top to rule out notifications for projects versions ending with -MR-12345 or -MR12345. We also run dependency track analysis on merge request build pipelines, for that we create temporary projects but we don't what violations to show up on Teams (they are shown in the MR though).
FYI, for people working on their own template. You might want to add the following part to the attachment on the level of $schema. It will make the card full-with in Teams:
"msteams": {
"width": "Full"
},
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5"
From what I can see in my testing is that both the old "Office Connector" webhooks and the new "Power Automate/Workflow" webhooks accept the Adaptive Cards format. Dependency Track could switch to the format and be ready for whatever/whenever Micorosoft phases out the old webhook urls. That's what we're doing in https://github.com/DefectDojo/django-DefectDojo/pull/13082
Well that would be the best-case scenario indeed. Thanks for the heads-up @valentijnscholten
Since this will break 11/20/2025 is there an update?
I don't see a new end date yet on the ms blogpost?
Pretty meaningless without a link...