Objective
You have a working FlexDeploy Release/Pipeline. The goal of the tutorial is to send notifications to Microsoft Teams based on certain Release/Pipeline milestones being achieved. The tutorial will include:
Configure credentials in Azure
Configure the Messaging Integration Account in FlexDeploy
Configure FlexDeploy Webhooks to send messages to Teams
Configure credentials in Azure
We would need to perform 3 primary activities,
One valid regular Azure user without MFA enabled. Preferably create a dedicated user account for the same.
A Service Principal(Azure Application), to handle authorization and send the messages on Teams on behalf of the user.
Provide all relevant access to the Service Principal so that it can send messages to Teams. Admin Consent/User Consent is essential as well.
Please refer to the below link on how to configure Service Principal(Azure Application) and provide relevant permissions.
Configuring FlexDeploy for Teams Operations using User Credentials
During execution, both user and Azure Application credentials are used internally to generate an OAuth token which is used for authorization.
Configure Messaging Integration Account
From the above steps, we have a valid Azure user and Service Principal(ClientID/Client Secret).
Create a new FlexDeploy Messaging Instance with the relevant details.
Use Test Connection to validate if the connection is established or not.
In case of any failure please check the FlexDeploy logs to understand the issue.
Configure a FlexDeploy Webhook to send Teams notifications
It may sound a bit complex with the terminology but once we understand how to configure it, you will find it pretty straightforward.
First review the below URL to understand the steps to configure an Outbound Webhook.
Getting Started with Outgoing Webhooks
Basically, there are two parts to a Webhook,
Filter – This is the criteria that determines when the Webhook will be triggered.
Script – This script is responsible for sending the Teams Notification.
Let's understand in a bit more detail. Below are some of the FlexDeploy events that can trigger a Webhook. The complete list of available events can be found in the link below
Events
Filter
One event can be picked up by one Listener/Webhook only. So in case you have multiple Listeners configured with the same criteria (Filter), the Listener from the top of the list that satisfies the criteria (Filter) first will process the event message.
So it's absolutely essential to have a proper Event Message Filter. We have added the below filter.
if(EVENT.payload.environment.environmentCode.equals("DEV") && EVENT.payload.pipeline.pipelineName.equals("Teams_Dummy_Pipeline")) { return true; } else { return false; }
It’s evident from the logic that we want the Webhook to be triggered only when the Pipeline being executed is - Teams_Dummy_Pipeline and the Stage executed/Environment code is DEV
Now the obvious question that will arise is, where exactly are we getting the EVENT.payload object.
As mentioned in the earlier step, events get generated under certain conditions only. They do get generated as a JSON payload with relevant details, it’s processed by Webhook Listener.
Event Payload
You can get a detailed structure of the payload for each event from the below link.
EventsNavigate to the Messages section on the Outgoing Webhooks page to view a summary of all the previously processed events.
You can find the actual event message payload details and any available logs from the processing of the event here.
If this is your first time and you are still confused with the idea of the payload, you can start by creating Webhook without a filter. You will still be able to view the Event payloads. However, it is strongly recommended to always use some filtering in real-life scenarios.
Script - Send notification to Teams Channel
Using the same approach, evaluating the payload we can easily write our groovy script.
def listenerName = "Send Teams Notification on Stage Completion"; LOG.info("Running listener: ${listenerName}"); def builder = new StringBuilder(); def envCode=EVENT.payload.environment.environmentName def relName=EVENT.payload.release.releaseName def pipelineName=EVENT.payload.pipeline.pipelineName def executionStatus = EVENT.payload.pipelineStageExecution.executionStatus // Setting the Header of the message builder.append("<h1 style=\"padding: 10px 0\">Pipeline stage execution completed.</h1>"); // Sending the Stage execution details in Table format // Below section is to form the HTML table format builder.append("<table border=1>"); builder.append("<tr>"+ "<th style=\"padding: 0 10px 5px 0\">Environment Name</th>"+ "<th style=\"padding: 0 10px 5px 0\">Release Name</th>"+ "<th style=\"padding: 0 10px 5px 0\">Pipeline Name</th>"+ "<th style=\"padding: 0 10px 5px 0\">Stage Execution Status</th>"+ "</tr>"); builder.append("<tr>"+ "<td style=\"padding-right: 10px\">${envCode}</td>"+ "<td style=\"padding-right: 10px\">${relName}</td>"+ "<td style=\"padding-right: 10px\">${pipelineName}</td>"+ "<td style=\"padding-right: 10px\">${executionStatus}</td>"+ "</tr>"); builder.append("</table>"); // Mapping the SringBuilder content to String variable def htmlMessage = builder.toString(); // Log will be printed in FlexDeploy log for reference LOG.fine("Sending execution status for ${listenerName} to Teams: ${htmlMessage}" ); // Sending the message to Teams Channel // sendTeamsMessage operation signature // sendTeamsMessage(String pTeamsMessagingAccountCode, String pTeamName, String pChannelName, String pMessageText, String pIconURL) // Argument desription of sendTeamsMessage operation // * @param pTeamsMessagingAccountCode: The messaging account code for the Teams account. // Code of Messaging Instances under Integration // * @param pTeamName: Name of the Microsoft Team that will receive the message. // * @param pChannelName: Name of the Microsoft Team Channel that the message will be sent to. // * @param pMessageText: The text for the message content. Can be plain text or html. // * @param pIconURL: The URL for the icon of the sender of the message. MICROSOFTTEAMS.sendTeamsMessage("TEAMSTEST","Notification Team","General",htmlMessage,null);
Apparently, it may look like a huge /complex code, but it isn’t. Let us go through it.
It simply retrieves some information from the event payload and assigns it to 4 variables.
Then create a simple HTML table(basically String) for better clarity (not mandatory, clear text is also acceptable ).
Finally, use the sendTeamsMessage operation to send the message to the Teams channel.
A lot of code sections are just comments to give you better clarity.
Just to make it clear, the MessagingAccountCode is one that we have already configured under FlexDeploy integration as mentioned under Configure Messaging Integration Account
Once the Pipeline stage is executed, we can find the processing logs and the event payload being processed successfully by our Webhook.
As a result, we can find the below message in Teams Channel.
Debugging execution
It can happen, that there could be an issue with the script. In such a scenario, navigate to the Messages page and use the actions menu to view the logs.
Script - Send Notification to individual users
Now we will send a notification to an individual user. We select the Event which will be generated whenever a Release Gets Started. The filter being used can be seen below as well.
We will try something slightly more complex here. In our Release, we have 4 Projects configured as shown below.
We want to print all project details as part of our Team message. The implementation will be similar to our previous example. We are just adding a loop to capture details for all Projects.
EVENT.payload.projects.each{ projectSet -> LOG.fine("Processing for Project name: ${projectSet.projectName}"); // Retrieving individual Project Name and Project Group def projName = projectSet.projectName; def projGroupName = projectSet.groupName; // Below section is to form the HTML table row for each Project builder.append("<tr>"+ "<td style=\"padding-right: 10px\">${projName}</td>"+ "<td style=\"padding-right: 10px\">${projGroupName}</td>"+ "<td style=\"padding-right: 10px\">${pipelineName}</td>"+ "<td style=\"padding-right: 10px\">${relName}</td>"+ "</tr>"); }
In the above code, the projectSet variable is just acting as an iterator, looping through all Project objects and adding rows to the HTML table with individual Project details.
Below given is the entire code. This time we will use the sendUserTeamsMessage operation to send the message to specific users.
def listenerName = "Send Teams Notification on Stage Completion-EBS"; LOG.info("Running listener: ${listenerName}"); def builder = new StringBuilder(); def relName=EVENT.payload.release.releaseName def pipelineName=EVENT.payload.pipeline.pipelineName // Setting the Header of the message builder.append("<h1 style=\"padding: 10px 0\">EBS-Pipeline stage execution completed.</h1>"); // Sending the Stage execution details in Table format builder.append("<table border=1>"); builder.append("<tr>"+ "<th style=\"padding: 0 10px 5px 0\">Project Name</th>"+ "<th style=\"padding: 0 10px 5px 0\">Project Group</th>"+ "<th style=\"padding: 0 10px 5px 0\">Pipeline Name</th>"+ "<th style=\"padding: 0 10px 5px 0\">Release Name</th>"+ "</tr>"); // Loop through all Projects EVENT.payload.projects.each{ projectSet -> LOG.fine("Processing for Project name: ${projectSet.projectName}"); // Retrieving individual Project Name and Project Group def projName = projectSet.projectName; def projGroupName = projectSet.groupName; // Below section is to form the HTML table row for each Project builder.append("<tr>"+ "<td style=\"padding-right: 10px\">${projName}</td>"+ "<td style=\"padding-right: 10px\">${projGroupName}</td>"+ "<td style=\"padding-right: 10px\">${pipelineName}</td>"+ "<td style=\"padding-right: 10px\">${relName}</td>"+ "</tr>"); } builder.append("</table>"); // Mapping the SringBuilder content to String variable def htmlMessage = builder.toString(); // Log will be printed in FlexDeploy log for reference LOG.fine("Sending execution status for ${listenerName} to Teams: ${htmlMessage}" ); // Sending the message to Teams user // sendUserTeamsMessage operation signature // sendUserTeamsMessage(String pTeamsMessagingAccountCode, String pTeamName, String pChannelName, String pMessageText, String pIconURL) // Argument desription of sendUserTeamsMessage operation // * @param pTeamsMessagingAccountCode: The messaging account code for the Teams account. // Code of Messaging Instances under Integration // * @param pTeamName: Name of the Microsoft Team that will receive the message. // * @param pChannelName: Name of the Microsoft Team Channel that the message will be sent to. // * @param pMessageText: The text for the message content. Can be plain text or html. // * @param pIconURL: The URL for the icon of the sender of the message. MICROSOFTTEAMS.sendUserTeamsMessage("TEAMSTEST","ToBeUpdated@domain.com",htmlMessage);
Once the Release is triggered, a message like the one shown below will be received by the user.
A similar example with the Test Results being sent to Teams can be found in the link below.
Congratulations! You have successfully completed the Teams Notification Integration with FlexDeploy.
Now that you have completed Teams setup successfully in FlexDeploy, the same can be reused for all other Teams notifications as well. Simply configure your Teams message filter and content as mentioned above and FlexDeploy will take care of the rest.