Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion incubating/msteams-notifier/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.9.12-alpine3.15
FROM python:3.14.6-alpine3.23

ENV LANG C.UTF-8

Expand All @@ -11,6 +11,10 @@ RUN apk update && \
nodejs && \
pip install --no-cache-dir pymsteams==$PYMSTEAMS_VERSION

# pymsteams-workflow is vendored locally (see script/pymsteams_workflow.py) because
# the PyPI package installs under the module name "pymsteams" and collides with the
# legacy webhook client above.
COPY script/pymsteams-notifier.py /pymsteams-notifier.py
COPY script/pymsteams_workflow.py /pymsteams_workflow.py

ENTRYPOINT ["python", "/pymsteams-notifier.py"]
33 changes: 33 additions & 0 deletions incubating/msteams-notifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,35 @@ Codefresh Pipeline Step to Send Notification to Microsoft Teams
EXAMPLE CARD
![Microsoft Teams Example Card](/incubating/msteams-notifier/images/msteams_example_card.png)

> **⚠️ Office 365 Connectors are being retired by Microsoft.**
> The legacy Incoming Webhook (`MSTEAMS_WEBHOOK_URL`) relies on Office 365 connectors, which Microsoft is
> retiring within Microsoft Teams. New connector webhooks can no longer be created and existing ones will
> stop working. See the official announcement for timelines and details:
> https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/
>
> Use **Power Automate Workflows** instead (see the section below).

## Power Automate Workflows (recommended)

Microsoft's replacement for retired Office 365 connectors is a Power Automate Workflow with the
**"Post to a channel when a webhook request is received"** trigger. To use this mode you must:

1. Set `USE_POWER_AUTOMATE_WORKFLOWS=true` to switch the step from the legacy webhook to the Workflow client.
2. Provide the Workflow trigger URL via `MSTEAMS_WORKFLOW_URL` (this replaces `MSTEAMS_WEBHOOK_URL`).

``` yaml
MSTeamsNotification:
image: codefreshplugins/cfstep-msteams-notifier:latest
environment:
- USE_POWER_AUTOMATE_WORKFLOWS=true
- MSTEAMS_WORKFLOW_URL=https://prod-XX.westus.logic.azure.com:443/workflows/...
```

`USE_POWER_AUTOMATE_WORKFLOWS` defaults to `false`; when it is not `true` the step falls back to the legacy
webhook behaviour described below.

## Legacy Incoming Webhook (deprecated)

YAML Step
``` yaml
MSTeamsNotification:
Expand All @@ -23,6 +52,10 @@ Replace the MSTEAMS_WEBHOOK_URL value in the Basic YAML example with the URL pro

This is the only required variable for the notification to send out on a pipeline execution.

> **Note:** This mode is deprecated. Because Office 365 connectors are being retired
> ([announcement](https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/)),
> prefer the Power Automate Workflows mode above for new pipelines.

TODO: Add links to Codefresh imagery for connector or card usage.

Want to send specific notifications based on the pipeline failing or succeeding?
Expand Down
1 change: 1 addition & 0 deletions incubating/msteams-notifier/example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ steps:
MSTeamsNotification:
image: codefreshplugins/cfstep-msteams-notifier:latest
environment:
- USE_POWER_AUTOMATE_WORKFLOWS=true
- MSTEAMS_WEBHOOK_URL=https://outlook.office.com/webhook/37a4ea3d...
8 changes: 7 additions & 1 deletion incubating/msteams-notifier/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,11 @@ envs:
type: optional
description: Custom Title, Default [Codefresh Build Notification]
- name: MSTEAMS_WEBHOOK_URL
type: required
type: optional
description: Microsoft Teams Webhook (connector) URL
- name: USE_POWER_AUTOMATE_WORKFLOWS
type: optional
description: To use Power Automate Workflow, Default [false]
- name: MSTEAMS_WORKFLOW_URL
type: optional
description: Microsoft Teams Workflow URL
95 changes: 90 additions & 5 deletions incubating/msteams-notifier/script/pymsteams-notifier.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import json
import os
import pymsteams
import pymsteams_workflow


def main():
def msteamsNotifier():
cf_account = os.getenv('CF_ACCOUNT')
cf_commit_author = os.getenv('CF_COMMIT_AUTHOR')
cf_branch = os.getenv('CF_BRANCH')
Expand All @@ -25,7 +26,11 @@ def main():
msteams_new_webhook_url = os.getenv('MSTEAMS_NEW_WEBHOOK_URL')
msteams_text = os.getenv('MSTEAMS_TEXT', 'Codefresh Account: {}'.format(cf_account))
msteams_title = os.getenv('MSTEAMS_TITLE', 'Codefresh Build Notification')
msteams_webhook_url = os.getenv('MSTEAMS_WEBHOOK_URL')
msteams_webhook_url = os.getenv('MSTEAMS_WEBHOOK_URL', '')

if msteams_webhook_url == "":
print("ERROR: MSTEAMS_WEBHOOK_URL is not set")
exit(1)

# You must create the connectorcard object with the Microsoft Webhook URL
myTeamsMessage = pymsteams.connectorcard(msteams_webhook_url)
Expand All @@ -39,11 +44,11 @@ def main():
# Add button and link to the message.
if msteams_link_url:
myTeamsMessage.addLinkButton(msteams_link_text, msteams_link_url)

# Add button and link to the message.
if msteams_link_url_2:
myTeamsMessage.addLinkButton(msteams_link_text_2, msteams_link_url_2)
myTeamsMessage.addLinkButton(msteams_link_text_2, msteams_link_url_2)

# create the section
myMessageSection = pymsteams.cardsection()

Expand Down Expand Up @@ -80,6 +85,86 @@ def main():
# send the message.
myTeamsMessage.send()

def msteamsWorkflows():
cf_account = os.getenv('CF_ACCOUNT')
cf_commit_author = os.getenv('CF_COMMIT_AUTHOR')
cf_branch = os.getenv('CF_BRANCH')
cf_build_url = os.getenv('CF_BUILD_URL')
cf_commit_message = os.getenv('CF_COMMIT_MESSAGE')
cf_commit_url = os.getenv('CF_COMMIT_URL')
cf_pull_request_action = os.getenv('CF_PULL_REQUEST_ACTION')
cf_pull_request_number = os.getenv('CF_PULL_REQUEST_NUMBER')
cf_status_message = os.getenv('CF_STATUS_MESSAGE', 'EXECUTED')
cf_repo_name = os.getenv('CF_REPO_NAME')
cf_revision = os.getenv('CF_REVISION')
msteams_activity_image = os.getenv('MSTEAMS_ACTIVITY_IMAGE', 'https://steps.codefresh.io/assets/img/loading.gif')
mstreams_activity_subtitle = os.getenv('MSTEAMS_ACTIVITY_SUBTITLE', 'Build Status: {}'.format(cf_status_message))
msteams_activity_text = os.getenv('MSTEAMS_ACTIVITY_TEXT', 'Additional Information Below')
msteams_link_text = os.getenv('MSTEAMS_LINK_TEXT', 'Codefresh Build Logs')
msteams_link_text_2 = os.getenv('MSTEAMS_LINK_TEXT_2', 'Commit Information')
msteams_link_url = os.getenv('MSTEAMS_LINK_URL', cf_build_url)
msteams_link_url_2 = os.getenv('MSTEAMS_LINK_URL_2', cf_commit_url)
msteams_new_workflow_url = os.getenv('MSTEAMS_NEW_WORKFLOW_URL')
msteams_text = os.getenv('MSTEAMS_TEXT', 'Codefresh Account: {}'.format(cf_account))
msteams_title = os.getenv('MSTEAMS_TITLE', 'Codefresh Build Notification')
msteams_workflow_url = os.getenv('MSTEAMS_WORKFLOW_URL')

myTeamsMessage = pymsteams_workflow.connectorcard(msteams_workflow_url)
# Add title to the message
myTeamsMessage.title(msteams_title)

# Add text to the message.
myTeamsMessage.text(msteams_text)

# Add button and link to the message.
if msteams_link_url:
myTeamsMessage.addLinkButton(msteams_link_text, msteams_link_url)

# Add button and link to the message.
if msteams_link_url_2:
myTeamsMessage.addLinkButton(msteams_link_text_2, msteams_link_url_2)

# create the section
myMessageSection = pymsteams_workflow.cardsection()

# Activity Elements
myMessageSection.activitySubtitle(mstreams_activity_subtitle)
myMessageSection.activityImage(msteams_activity_image)
myMessageSection.activityText(msteams_activity_text)

# Facts are key value pairs displayed in a list.
if cf_repo_name:
myMessageSection.addFact("GIT Repository", cf_repo_name)
if cf_branch:
myMessageSection.addFact("GIT Branch", cf_branch)
if cf_revision:
myMessageSection.addFact("GIT Revision", cf_revision)
if cf_commit_author:
myMessageSection.addFact("Commit Author", cf_commit_author)
if cf_commit_message:
myMessageSection.addFact("Commit Message", cf_commit_message)
if cf_pull_request_number:
myMessageSection.addFact("Pull Request Number", cf_pull_request_number)
if cf_pull_request_action:
myMessageSection.addFact("Pull Request Action", cf_pull_request_action)

# Add your section to the connector card object before sending
myTeamsMessage.addSection(myMessageSection)

myTeamsMessage.printme()

# send the message.
myTeamsMessage.send()



def main():
useWorkflows = os.getenv("USE_POWER_AUTOMATE_WORKFLOWS", "false").lower()
if useWorkflows == "true":
msteamsWorkflows()
else:
msteamsNotifier()


if __name__ == "__main__":
main()
Loading
Loading