jenkinsci / office-365-connector-plugin

Office 365 Connector plugin sends jobs status notifications to Microsoft Teams or Outlook
https://plugins.jenkins.io/Office-365-Connector/
Apache License 2.0
87 stars 82 forks source link

New "workflows" based webhooks are failing #353

Open robations opened 1 month ago

robations commented 1 month ago

Jenkins and plugins versions report

Jenkins: 2.440.1
OS: Linux - 5.4.0-182-generic
Java: 11.0.23 - Ubuntu (OpenJDK 64-Bit Server VM)
--- Office-365-Connector:4.21.1 ace-editor:1.1 antisamy-markup-formatter:162.v0e6ec0fcfcf6 apache-httpcomponents-client-4-api:4.5.14-208.v438351942757 asm-api:9.7-33.v4d23ef79fcc8 authentication-tokens:1.113.v81215a_241826 bitbucket-build-status-notifier:1.4.2 bitbucket-oauth:0.13 bootstrap4-api:4.6.0-6 bootstrap5-api:5.3.3-1 bouncycastle-api:2.30.1.78.1-233.vfdcdeb_0a_08a_a_ branch-api:2.1169.va_f810c56e895 build-timeout:1.32 caffeine-api:3.1.8-133.v17b_1ff2e0599 checks-api:2.2.0 cloudbees-folder:6.901.vb_4c7a_da_75da_3 clover:4.14.2.596.vb_4d6475e990b_ command-launcher:107.v773860566e2e commons-httpclient3-api:3.1-3 commons-lang3-api:3.14.0-76.vda_5591261cfe commons-text-api:1.12.0-119.v73ef73f2345d config-file-provider:973.vb_a_80ecb_9a_4d0 credentials:1337.v60b_d7b_c7b_c9f credentials-binding:677.vdc9d38cb_254d data-tables-api:2.0.8-1 display-url-api:2.204.vf6fddd8a_8b_e9 docker-commons:439.va_3cb_0a_6a_fb_29 docker-workflow:580.vc0c340686b_54 durable-task:555.v6802fe0f0b_82 echarts-api:5.5.0-1 eddsa-api:0.3.0-4.v84c6f0f4969e email-ext:1814.v404722f34263 extended-choice-parameter:382.v5697b_32134e8 external-monitor-job:215.v2e88e894db_f8 font-awesome-api:6.5.2-1 git:5.2.2 git-client:4.7.0 git-server:126.v0d945d8d2b_39 github:1.39.0 github-api:1.318-461.v7a_c09c9fa_d63 github-branch-source:1789.v5b_0c0cea_18c3 gson-api:2.11.0-41.v019fcf6125dc handlebars:3.0.8 instance-identity:185.v303dc7c645f9 ionicons-api:74.v93d5eb_813d5f jackson2-api:2.17.0-379.v02de8ec9f64c jakarta-activation-api:2.1.3-1 jakarta-mail-api:2.1.3-1 javax-activation-api:1.2.0-7 javax-mail-api:1.6.2-10 jaxb:2.3.9-1 jdk-tool:73.vddf737284550 jjwt-api:0.11.5-112.ve82dfb_224b_a_d joda-time-api:2.12.7-29.v5a_b_e3a_82269a_ jquery:1.12.4-1 jquery3-api:3.7.1-2 jsch:0.2.16-86.v42e010d9484b_ json-api:20240303-41.v94e11e6de726 json-path-api:2.9.0-58.v62e3e85b_a_655 junit:1265.v65b_14fa_f12f0 ldap:725.v3cb_b_711b_1a_ef list-git-branches-parameter:0.0.13 lockable-resources:1255.vf48745da_35d0 mailer:472.vf7c289a_4b_420 mapdb-api:1.0.9-40.v58107308b_7a_7 matrix-auth:3.2.2 matrix-project:822.824.v14451b_c0fd42 mercurial:1260.vdfb_723cdcc81 mina-sshd-api-common:2.12.1-113.v4d3ea_5eb_7f72 mina-sshd-api-core:2.12.1-113.v4d3ea_5eb_7f72 momentjs:1.1.1 multiple-scms:0.8 nodejs:1.6.1 okhttp-api:4.11.0-172.vda_da_1feeb_c6e pam-auth:1.11 pipeline-build-step:540.vb_e8849e1a_b_d8 pipeline-graph-analysis:216.vfd8b_ece330ca_ pipeline-groovy-lib:727.ve832a_9244dfa_ pipeline-input-step:495.ve9c153f6067b_ pipeline-milestone-step:119.vdfdc43fc3b_9a_ pipeline-model-api:2.2198.v41dd8ef6dd56 pipeline-model-definition:2.2198.v41dd8ef6dd56 pipeline-model-extensions:2.2198.v41dd8ef6dd56 pipeline-rest-api:2.34 pipeline-stage-step:312.v8cd10304c27a_ pipeline-stage-tags-metadata:2.2198.v41dd8ef6dd56 pipeline-stage-view:2.34 plain-credentials:182.v468b_97b_9dcb_8 plugin-util-api:4.1.0 popper-api:1.16.1-3 popper2-api:2.11.6-4 resource-disposer:0.23 scm-api:690.vfc8b_54395023 script-security:1341.va_2819b_414686 slack:722.vd07f1ea_7ff40 snakeyaml-api:2.2-111.vc6598e30cc65 ssh-credentials:337.v395d2403ccd4 ssh-slaves:2.973.v0fa_8c0dea_f9f sshd:3.330.vc866a_8389b_58 structs:337.v1b_04ea_4df7c8 subversion:1256.vee91953217b_6 timestamper:1.27 token-macro:400.v35420b_922dcb_ trilead-api:2.147.vb_73cc728a_32e variant:60.v7290fc0eb_b_cd workflow-aggregator:596.v8c21c963d92d workflow-api:1316.v33eb_726c50b_a_ workflow-basic-steps:1058.vcb_fc1e3a_21a_9 workflow-cps:3903.v48a_8836749e9 workflow-cps-global-lib:612.v55f2f80781ef workflow-durable-task-step:1353.v1891a_b_01da_18 workflow-job:1400.v7fd111b_ec82f workflow-multibranch:773.vc4fe1378f1d5 workflow-scm-step:427.v4ca_6512e7df1 workflow-step-api:657.v03b_e8115821b_ workflow-support:907.v6713a_ed8a_573 ws-cleanup:0.46

What Operating System are you using (both controller, and any agents involved in the problem)?

Server: Ubuntu 20.04.6 LTS Me: MacOS Microsoft: unknown

Reproduction steps

  1. Add a new webhook to a Teams channel
  2. The only way to do this now is "Workflows" from the channel context menu, which uses Power Automate
  3. Create a webhook using "Post to a channel when a webhook request is received", which seems to be the most relevant
  4. This creates a "flow" where you can eventually find a webhook URL (although Microsoft’s interface seems buggy)
  5. Add the URL in Jenkins under job configuration > Office 365 Connector > Notification webhooks (and selected relevant statuses)
  6. Build failures from the job come through with an error:
    • ExpressionEvaluationFailed. 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.

Expected Results

Expected to receive a message (new thread) in the channel.

Actual Results

Every webhook request failed with error:

ExpressionEvaluationFailed

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.

Anything else?

I realise this could be an issue with the Power Automate configuration, but at best the documentation might need to be adjusted.

Are you interested in contributing a fix?

Perhaps if it's documentation-based, but I don't have specialist knowledge of either Power Automate or Jenkins.

robations commented 1 month ago

NB I've managed to work around in my case by finding an older webhook URL for the same channel (in the format https://acmecom.webhook.office.com/webhookb2/[UUID]/IncomingWebhook/[hash]/[UUID]).

For reference, the new URL (that is failing) looks like:

https://prod-XX.uksouth.logic.azure.com:443/workflows/[hash]/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=[base64 sig]
robations commented 1 month ago

Okay, I now see there is an app "Incoming Webhooks" app, which can be "added" to a Team. I can now use this to add the older style of webhook to a specified Team/channel, which works correctly. However, after adding the app, the UI for this completely disappears, and the only way I seem to be able to manage previous webhooks is by adding the app again. Still investigating.

I realise this is only tangentially related to the current project, but hope it might help someone following the same path.

agriessbosch commented 2 weeks ago

@robations the old webhooks will be retired soon: https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/ So I assume this will become a more pressing issue come August 2024.

NiklasPor commented 2 weeks ago

FYI the format of the payload for the format changed. The adaptive cards must be wrapped in a smalll data object now:

  {
       "type":"message",
       "attachments":[
          {
             "contentType":"application/vnd.microsoft.card.adaptive",
             "contentUrl":null,
             "content":{
                "$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
                "type":"AdaptiveCard",
                "version":"1.2",
                "body":[
                    {
                    "type": "TextBlock",
                    "text": "For Samples and Templates, see [https://adaptivecards.io/samples](https://adaptivecards.io/samples)"
                    }
                ]
             }
          }
       ]
    }
josegabrielrivera commented 1 week ago

Maybe this comment should be redirected to Microsoft Teams, but it could be useful for this development too:

With the new Workflows approach I think we are forced to create a flow for each channel.

I've found a hacky way to read the team and channel ID from the "content" object of the card payload:

{
    "type":"message",
    "attachments":[
        {
            "contentType":"application/vnd.microsoft.card.adaptive",
            "contentUrl":null,
            "content":{
                "$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
                "team_id": "<team_id>",
                "channel_id": "<channel_id>",
                "type":"AdaptiveCard",
                "version":"1.2",
                "body":[
                    {
                        "type": "TextBlock",
                        "text": "For Samples and Templates, see [https://adaptivecards.io/samples](https://adaptivecards.io/samples)"
                    }
                ]
            }
        }
    ]
}

Then, on the "Post Card" Teams Flow step: image

I'm not sure if there is a way to avoid creating one flow per channel, or if adding two new fields to this plugin to specify the ID's would be possible, but for teams with many channels to notify it would be a nightmare to manage it the standard way.

NiklasPor commented 1 week ago

@josegabrielrivera Awesome "hack" 👍

Did you also find a way to replace the subject line inside the notifications? In the past the webhook connector would show a small preview, now it always just states "Send a card" 😢

ViliusS commented 1 week ago

Just started to receive notifications regarding deprecation of an old method too: image

Did someone manage to make it work with the new Workflows method? From @NiklasPor comment above it looks like plugin needs to be adjusted, or does this mean that Workflow configuration should be adjusted to correctly parse the data sent by the plugin?

karannnn-exe commented 1 week ago

any updates how to use this with the new workflow mechanism ? image

RocketRacer commented 1 week ago

I also found out that if you go into the "Post a message to a channel" Workflow itself you can see that it uses an action to send an 'adaptive card' by default, but you can change it to post a 'message' instead: image Unlike Adaptive Card, posting a message seems to be just raw text that you sent it but it uses HTML formatting like the Webhooks do (Adaptive Card does not understand my HTML formatting from the testing that I've done). Perhaps this method could be used to receive and post messages that resemble the Webhook messages instead of using the Adaptive Card system.

But yea, regardless of the approach it's already a pain to migrate everything to Workflows because Workflows bot can't post to private/shared channels, only public channels, so if you have multiple private channels set up for different kind of notifications (regardless if it's Jenkins or just Incoming Webhook on its own) you have to move everything to public channels

https://ideas.powerautomate.com/d365community/idea/688f49e7-711e-49e9-b4c6-7560bdb3e17e

jimmygoogle commented 1 week ago

I also found out that if you go into the "Post a message to a channel" Workflow itself you can see that it uses an action to send an 'adaptive card' by default, but you can change it to post a 'message' instead: image Unlike Adaptive Card, posting a message seems to be just raw text that you sent it but it uses HTML formatting like the Webhooks do (Adaptive Card does not understand my HTML formatting from the testing that I've done). Perhaps this method could be used to receive and post messages that resemble the Webhook messages instead of using the Adaptive Card system.

But yea, regardless of the approach it's already a pain to migrate everything to Workflows because Workflows bot can't post to private/shared channels, only public channels, so if you have multiple private channels set up for different kind of notifications (regardless if it's Jenkins or just Incoming Webhook on its own) you have to move everything to public channels

https://ideas.powerautomate.com/d365community/idea/688f49e7-711e-49e9-b4c6-7560bdb3e17e

The webhook expects a payload like this. It will fail to send a message unless it gets a valid payload. I just spent a few hours updating our code to send proper webhooks. I looked at the o365 code but I am not a Java guy or else I would take a stab at this. This is another helpful link too for the TextBlock.


        "type"        : "message",
        "attachments" : [
            {
                contentType : 'application/vnd.microsoft.card.adaptive',
                content     : {
                    '$schema' : 'http://adaptivecards.io/schemas/adaptive-card.json',
                    type      : "AdaptiveCard",
                    version   : "1.2",
                    body      : [
                        {
                            type : 'TextBlock',
                            text : 'i am some message
                        }
                    ]
                }
            }
        ]
    }
RocketRacer commented 1 week ago

I also found out that if you go into the "Post a message to a channel" Workflow itself you can see that it uses an action to send an 'adaptive card' by default, but you can change it to post a 'message' instead: image Unlike Adaptive Card, posting a message seems to be just raw text that you sent it but it uses HTML formatting like the Webhooks do (Adaptive Card does not understand my HTML formatting from the testing that I've done). Perhaps this method could be used to receive and post messages that resemble the Webhook messages instead of using the Adaptive Card system. But yea, regardless of the approach it's already a pain to migrate everything to Workflows because Workflows bot can't post to private/shared channels, only public channels, so if you have multiple private channels set up for different kind of notifications (regardless if it's Jenkins or just Incoming Webhook on its own) you have to move everything to public channels https://ideas.powerautomate.com/d365community/idea/688f49e7-711e-49e9-b4c6-7560bdb3e17e

The webhook expects a payload like this. It will fail to send a message unless it gets a valid payload. I just spent a few hours updating our code to send proper webhooks. I looked at the o365 code but I am not a Java guy or else I would take a stab at this. This is another helpful link too for the TextBlock.

        "type"        : "message",
        "attachments" : [
            {
                contentType : 'application/vnd.microsoft.card.adaptive',
                content     : {
                    '$schema' : 'http://adaptivecards.io/schemas/adaptive-card.json',
                    type      : "AdaptiveCard",
                    version   : "1.2",
                    body      : [
                        {
                            type : 'TextBlock',
                            text : 'i am some message
                        }
                    ]
                }
            }
        ]
    }

Yeah, the cards webhook expects that format, but you can switch to a message webhook instead and that one will expect a different payload. However I think the cards one might be better overall. There's also this useful page too - online Designer of the Adaptive Cards, you can make some decent designs with this, which is nice. Just put whatever is generated into the "contents" block of the payload, but from my testing the latest version of the schema we can use in Teams right now is 1.4. I'm using this to update our notifications and format them nicely.

juancamilocc commented 1 week ago

I'm working this way, I've chose this flow, following the steps to set up. image After that, I passed the parameters from the channel that I want, the channel must be standard type, so the flow is this. image And I'm managing this payload

{
    "attachments": [
      {
        "contentType": "application/vnd.microsoft.card.adaptive",
        "content": {
          "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
          "type": "AdaptiveCard",
          "version": "1.3",
          "body": [
            {
              "type": "TextBlock",
              "size": "Medium",
              "weight": "Bolder",
              "text": "${title}",
              "wrap": true
            },
            {
              "type": "ColumnSet",
              "columns": [
                {
                  "type": "Column",
                  "items": [
                    {
                      "type": "Image",
                      "style": "Person",
                      "url": "https://ftp-chi.osuosl.org/pub/jenkins/art/jenkins-logo/1024x1024/logo.png",
                      "altText": "Jenkins Notification",
                      "size": "Small"
                    }
                  ],
                  "width": "auto"
                },
                {
                  "type": "Column",
                  "items": [
                    {
                      "type": "TextBlock",
                      "weight": "Bolder",
                      "text": "Jenkins Notification",
                      "wrap": true
                    },
                    {
                      "type": "TextBlock",
                      "spacing": "None",
                      "text": "${date}",
                      "isSubtle": true,
                      "wrap": true
                    }
                  ],
                  "width": "stretch"
                }
              ]
            },
            {
              "type": "TextBlock",
              "weight": "Bolder",
              "color": "${colorStatus}",
              "text": "${status}",
              "wrap": true
            },
            {
              "type": "FactSet",
              "facts": [
                {
                  "title": "Key",
                  "value": "${value}"
                }
              ]
            }
          ],
          "actions": [
            {
              "type": "Action.OpenUrl",
              "title": "Ver compilación en Jenkins",
              "url": "${urlJenkinsLogs}"
            }
          ]
        }
      }
    ]
  }

Here, you can find more information https://adaptivecards.io/designer/ Finally, in the Jenkinsfile I've created a way to change the values in the json file, to send a notification that I want. image Ativating the webhook, making it the request

curl --location <webhook_url> \
--data-raw '<json_payload>'
karannnn-exe commented 1 week ago

I'm working this way, I've chose this flow, following the steps to set up. image After that, I passed the parameters from the channel that I want, the channel must be standard type, so the flow is this. image And I'm managing this payload

{
    "attachments": [
      {
        "contentType": "application/vnd.microsoft.card.adaptive",
        "content": {
          "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
          "type": "AdaptiveCard",
          "version": "1.3",
          "body": [
            {
              "type": "TextBlock",
              "size": "Medium",
              "weight": "Bolder",
              "text": "${title}",
              "wrap": true
            },
            {
              "type": "ColumnSet",
              "columns": [
                {
                  "type": "Column",
                  "items": [
                    {
                      "type": "Image",
                      "style": "Person",
                      "url": "https://ftp-chi.osuosl.org/pub/jenkins/art/jenkins-logo/1024x1024/logo.png",
                      "altText": "Jenkins Notification",
                      "size": "Small"
                    }
                  ],
                  "width": "auto"
                },
                {
                  "type": "Column",
                  "items": [
                    {
                      "type": "TextBlock",
                      "weight": "Bolder",
                      "text": "Jenkins Notification",
                      "wrap": true
                    },
                    {
                      "type": "TextBlock",
                      "spacing": "None",
                      "text": "${date}",
                      "isSubtle": true,
                      "wrap": true
                    }
                  ],
                  "width": "stretch"
                }
              ]
            },
            {
              "type": "TextBlock",
              "weight": "Bolder",
              "color": "${colorStatus}",
              "text": "${status}",
              "wrap": true
            },
            {
              "type": "FactSet",
              "facts": [
                {
                  "title": "Key",
                  "value": "${value}"
                }
              ]
            }
          ],
          "actions": [
            {
              "type": "Action.OpenUrl",
              "title": "Ver compilación en Jenkins",
              "url": "${urlJenkinsLogs}"
            }
          ]
        }
      }
    ]
  }

Here, you can find more information https://adaptivecards.io/designer/ Finally, in the Jenkinsfile I've created a way to change the values in the json file, to send a notification that I want. image

Can you share the jenkinsfile as well ? Like what have you used for sending notifications now ?

jimmygoogle commented 1 week ago

I'm working this way, I've chose this flow, following the steps to set up. image After that, I passed the parameters from the channel that I want, the channel must be standard type, so the flow is this. image And I'm managing this payload

{
    "attachments": [
      {
        "contentType": "application/vnd.microsoft.card.adaptive",
        "content": {
          "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
          "type": "AdaptiveCard",
          "version": "1.3",
          "body": [
            {
              "type": "TextBlock",
              "size": "Medium",
              "weight": "Bolder",
              "text": "${title}",
              "wrap": true
            },
            {
              "type": "ColumnSet",
              "columns": [
                {
                  "type": "Column",
                  "items": [
                    {
                      "type": "Image",
                      "style": "Person",
                      "url": "https://ftp-chi.osuosl.org/pub/jenkins/art/jenkins-logo/1024x1024/logo.png",
                      "altText": "Jenkins Notification",
                      "size": "Small"
                    }
                  ],
                  "width": "auto"
                },
                {
                  "type": "Column",
                  "items": [
                    {
                      "type": "TextBlock",
                      "weight": "Bolder",
                      "text": "Jenkins Notification",
                      "wrap": true
                    },
                    {
                      "type": "TextBlock",
                      "spacing": "None",
                      "text": "${date}",
                      "isSubtle": true,
                      "wrap": true
                    }
                  ],
                  "width": "stretch"
                }
              ]
            },
            {
              "type": "TextBlock",
              "weight": "Bolder",
              "color": "${colorStatus}",
              "text": "${status}",
              "wrap": true
            },
            {
              "type": "FactSet",
              "facts": [
                {
                  "title": "Key",
                  "value": "${value}"
                }
              ]
            }
          ],
          "actions": [
            {
              "type": "Action.OpenUrl",
              "title": "Ver compilación en Jenkins",
              "url": "${urlJenkinsLogs}"
            }
          ]
        }
      }
    ]
  }

Here, you can find more information https://adaptivecards.io/designer/ Finally, in the Jenkinsfile I've created a way to change the values in the json file, to send a notification that I want. image Ativating the webhook, making it the request

curl --location <webhook_url> \
--data-raw '<json_payload>'

Are you just bypassing the current method and executing the curl command in the build?

juancamilocc commented 1 week ago

I saved the .json file in the repository, so the Jenkisfile looks like this in post actions:

post {
        success {
            script {
                //Send teams notification
                withCredentials([string(credentialsId: 'teams-notifications', variable: 'webhook')]) {

                    def requestTeams = readFile('requestTeamsNotifications.json')

                    requestTeams = requestTeams.replace('${title}',             "La compilación de ${currentBuild.projectName} fue exitosa, desplegando cambios...")
                    requestTeams = requestTeams.replace('${date}',              "${DATE}")
                    requestTeams = requestTeams.replace('${status}',            'SUCCESS')
                    requestTeams = requestTeams.replace('${colorStatus}',       'good')
                    requestTeams = requestTeams.replace('${key}',               "${value}")
                    requestTeams = requestTeams.replace('${urlJenkinsLogs}',    "${currentBuild.absoluteUrl}")

                    writeFile file: 'requestTeamsNotifications.json', text: requestTeams

                    sh """
                        curl --location '${webhook}' \
                        --header 'Content-Type: application/json' \
                        --data @requestTeamsNotifications.json
                    """
                }
            }
        }   
        failure {
            script {
                //Send teams notification
                withCredentials([string(credentialsId: 'teams-notifications', variable: 'webhook')]) {

                    def requestTeams = readFile('requestTeamsNotifications.json')

                    requestTeams = requestTeams.replace('${title}',             "La compilación de ${currentBuild.projectName} fallo!")
                    requestTeams = requestTeams.replace('${date}',              "${DATE}")
                    requestTeams = requestTeams.replace('${status}',            'FAILURE')
                    requestTeams = requestTeams.replace('${colorStatus}',       'attention')
                    requestTeams = requestTeams.replace('${key}',               "${value}")
                    requestTeams = requestTeams.replace('${urlJenkinsLogs}',    "${currentBuild.absoluteUrl}")

                    writeFile file: 'requestTeamsNotifications.json', text: requestTeams

                    sh """
                        curl --location '${webhook}' \
                        --header 'Content-Type: application/json' \
                        --data @requestTeamsNotifications.json
                    """
                }
            }
        }
    }