Objective
You have a working FlexDeploy Release/Pipeline. The goal of the tutorial is to send notification to Microsoft Teams based on certain Release/Pipeline milestone being achieved. The tutorial will include:
Configure credentials in Azure
Configure Messaging Integration Account in FlexDeploy
Configure FlexDeploy Webhooks to send message 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), which will send the messages to the 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 below link on how to configure Service Principal(Azure Application) and provide relevant permissions.
Just for information: During execution both user and Azure Application credential is used internally to generate OAuth token which is used for authorization.
Configure Messaging Integration Account
From above steps we have a valid Azure user and Service Principal(ClientID/Client Secret).
Create a new FlexDeploy Messaging Instances with relevant details.
Use Test Connection to validate if the connection is established or not.
In case for any failure please check FlexDeploy log to understand the issue.
Configure a FlexDeploy Webhook to send Teams notifications
It may sound bit complex with the terminology but once we understand how to configure it , you will find it pretty straight forward.
First review below url to understand the steps to configure an Outbound Webhook.
Basically there are two parts of a Webhook,
Event Filter – This is the criteria which when satisfied the Webhook will be triggered.
Groovy Script – This script which is responsible to send the Teams Notification.
Lets understand in bit more details. Below are the FlexDeploy events which can trigger a Webhook.
Event Filter
One event can be picked up by one Listener/Webhook only. So in case you have multiple Listener configured with same criteria(Filter), the Listener from top of the list which satisfy the Event Filter will process the event message.
So its absolutely essential to have a proper Event Message Filter. We have added 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 which will arise is , where exactly are we getting the EVENT.payload object.
As mentioned in earlier step, events get generated under certain conditions only. Tthey 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 below link.
https://flexagon.atlassian.net/wiki/spaces/FD60/pages/9856635409/Events
Navigate to View Messages under Outgoing Webhook Listeners.
You can find the actual event message payload details from here.
Tips: If this is your first time and you are still confused with payload, you can start creating Webhook without filter. So that you shall be able to view the Event payloads. However in Real life scenario you should always use filter.
Groovy 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. Lets go through it.
It’ simply retrieving some information from event payload and assigning it to 4 variables.
Then creating a simple HTML table(basically String) for better clarity (not mandatory, clear text also acceptable ).
At last using sendTeamsMessage operation to send the message to Teams channel.
Lot of code section are just comments to give you better clarity.
Just to make it clear , the MessagingAccountCode is the one which we have already configured under FlexDeploy integration as mentioned under Configure Messaging Integration Account
Now once the Pipeline stage got executed, we can find below Message being processed successfully by our Webhook.
As a result we can find below message in Teams Channel.
Debugging execution
It can happen, that there could be issue with the script. In such scenario, navigate to the Outgoing Webhook Listener --> View Messages and Filter out your Webhook Listener as shown below.
Then select the failed one and check select the View Logs option to get clarity on where exactly it got failed.
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.
We provide the Filter as given below.
We would 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 Teams message. Below given is the generated event payload with 4 Project details.
The implementation will be similar. 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>"); }
On above code, ProjectSet 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 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 below message will be received by the user.
Another similar reference with Test Result given in below link