You can configure Octopus to send message to a Slack Workspace with the following process:
- Configure Octopus Deploy subscription to send a webhook.
- Configure a Slack App.
- Configure a tool to consume the webhook from Octopus and forward a message on to Slack.
A number of technologies can be used to consume the webhook from Octopus. This document uses an Azure Function App. Another alternative is to use Firebase Cloud Functions, and this is described in this blog.
Configure an Octopus subscription to send a webhook
Configure a subscription in Octopus to send any events that occur against the User
, User Role
, and Scoped User Role
documents:
As a starting point, the Payload URL is set to a value on RequestBin, which provides access to the JSON being sent by Octopus before the function is built.
Configure your Slack app
A Slack app must be configured to enable a message to be sent through to Slack.
-
In Slack, go to Your Apps and click Create New App.
-
Enter a useful App Name and select the relevant Development Slack Workspace and click Create App:
-
Select Incoming Webhooks from the Add features and functionality section:
-
Click Add New Webhook to Workspace:
-
Select the channel to post the messages to:
-
Copy the webhook URL, this is the value for the
SLACK_URI_APIKEY
environment variable on the Azure Function App:
Create an Azure Function App
Create the Function App in Azure
The Function App can be created via the Azure Portal, and ARM Template or with the Azure CLI .
To use the Azure CLI, create the Resource Group to contain the Function App:
az group create -l westeurope -n OctopusFunctions
Create the storage account for the Function App to use:
az storage account create -n octofuncstorage -g OctopusFunctions --sku Standard_LRS -l westeurope
Now create the Function App itself:
az functionapp create -g OctopusFunctions -n SubscriptionHandler -s octofuncstorage --functions-version 3 --runtime dotnet --consumption-plan-location westeurope
Write the Function App code
The code for this can be found in the samples repo.
The Octopus subscription has been created and set up to send data to RequestBin. Here’s the resulting JSON payload following creation of a new Service Account user:
{
"Timestamp": "2020-04-16T15:19:42.5410789+00:00",
"EventType": "SubscriptionPayload",
"Payload": {
"ServerUri": "https://samples.octopus.app",
"ServerAuditUri": "https://samples.octopus.app/#/configuration/audit?triggerGroups=Document&documentTypes=Users&documentTypes=UserRoles&documentTypes=ScopedUserRoles&from=2020-04-16T15%3a19%3a11.%2b00%3a00&to=2020-04-16T15%3a19%3a42.%2b00%3a00",
"BatchProcessingDate": "2020-04-16T15:19:42.3149941+00:00",
"Subscription": {
"Id": "Subscriptions-21",
"Name": "User and User Role Change Alert",
"Type": "Event",
"IsDisabled": false,
"EventNotificationSubscription": {
"Filter": {
"Users": [],
"Projects": [],
"ProjectGroups": [],
"Environments": [],
"EventGroups": [
"Document"
],
"EventCategories": [],
"EventAgents": [],
"Tenants": [],
"Tags": [],
"DocumentTypes": [
"Users",
"UserRoles",
"ScopedUserRoles"
]
},
"EmailTeams": [],
"EmailFrequencyPeriod": "01:00:00",
"EmailPriority": "Normal",
"EmailDigestLastProcessed": null,
"EmailDigestLastProcessedEventAutoId": null,
"EmailShowDatesInTimeZoneId": "UTC",
"WebhookURI": "https://xxxxxxxx.x.pipedream.net",
"WebhookTeams": [],
"WebhookTimeout": "00:00:10",
"WebhookHeaderKey": null,
"WebhookHeaderValue": null,
"WebhookLastProcessed": "2020-04-16T15:19:11.3637275+00:00",
"WebhookLastProcessedEventAutoId": 53624
},
"SpaceId": "Spaces-142",
"Links": {
"Self": {}
}
},
"Event": {
"Id": "Events-55136",
"RelatedDocumentIds": [
"Users-322"
],
"Category": "Created",
"UserId": "Users-27",
"Username": "xxxxxxxx@octopus.com",
"IsService": false,
"IdentityEstablishedWith": "Session cookie",
"UserAgent": "OctopusClient-js/2020.2.2",
"Occurred": "2020-04-16T15:19:14.9925786+00:00",
"Message": "User SubTest Service Account has been created ",
"MessageHtml": "User <a href='#/configuration/users/Users-322'>SubTest Service Account</a> has been created ",
"MessageReferences": [
{
"ReferencedDocumentId": "Users-322",
"StartIndex": 5,
"Length": 23
}
],
"Comments": null,
"Details": null,
"SpaceId": null,
"Links": {
"Self": {}
}
},
"BatchId": "b2bdb09f-872a-4bc1-8272-ab2334de3660",
"TotalEventsInBatch": 2,
"EventNumberInBatch": 1
}
}
The items that will be used in the Slack message for this example are:
- Payload.Event.Message
- Payload.Event.SpaceId
- Payload.Event.Username
- Payload.ServerUri
If you’re using VS Code to write the code, you need to install the Azure Functions Core Tools to enable debugging for your function.
To use these items easily, create a class OctoMessage
to hold them:
using System;
using Newtonsoft.Json;
namespace Octopus
{
[JsonConverter(typeof(JsonPathConverter))]
public class OctoMessage
{
[JsonProperty("Payload.Event.Message")]
public string Message {get;set;}
[JsonProperty("Payload.Event.SpaceId")]
public string SpaceId {get;set;}
[JsonProperty("Payload.Event.Username")]
public string Username {get;set;}
[JsonProperty("Payload.ServerUri")]
public string ServerUri{get;set;}
public string GetSpaceUrl(){
return string.Format("{0}/app#/{1}",ServerUri,SpaceId);
}
}
}
Add a class SlackClient
to take the message data and send it through to Slack:
public class SlackClient
{
private readonly Uri _uri;
private readonly Encoding _encoding = new UTF8Encoding();
public SlackClient(string slackUrlWithAccessToken)
{
_uri = new Uri(slackUrlWithAccessToken);
}
public string PostMessage(string text)
{
Payload payload = new Payload()
{
Text = text
};
return PostMessage(payload);
}
public string PostMessage(Payload payload)
{
string payloadJson = JsonConvert.SerializeObject(payload);
using (WebClient client = new WebClient())
{
var data = new NameValueCollection();
data["payload"] = payloadJson;
var response = client.UploadValues(_uri, "POST", data);
return _encoding.GetString(response);
}
}
}
The main class of the function OctopusSlackHttpTrigger
, will receive the HTTP message, take the request message body and deserialize it into an OctoMessage
object. Next, it will build the message text and send it through to Slack using a SlackClient
object:
public static class OctopusSlackHttpTrigger
{
[FunctionName("OctopusSlackHttpTrigger")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log)
{
var client = new SlackClient(Environment.GetEnvironmentVariable("SLACK_URI_APIKEY"));
var data = await new StreamReader(req.Body).ReadToEndAsync();
var octoMessage = JsonConvert.DeserializeObject<OctoMessage>(data);
var slackMessage = string.Format(
"{0} (by {1}) - <{2}|Go to Octopus>",
octoMessage.Message,
octoMessage.Username,
octoMessage.GetSpaceUrl());
try
{
var responseText = client.PostMessage(text: slackMessage);
return new OkObjectResult(responseText);
}
catch (System.Exception ex)
{
log.LogError(ex.Message);
return new BadRequestObjectResult(ex.Message);
}
}
}
Test the Azure App Function
Before pushing this to Azure it can be tested locally. The Run
method uses the environment variable SLACK_URI_APIKEY
, this is the value copied when the Slack app was configured. In order to use this value when debugging locally, add the value to the local.settings.json
file:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"SLACK_URI_APIKEY":"https://hooks.slack.com/services/XXXXXXXX/XXXXXX/XXXXXXXXXXXXXXX"
}
}
Hit F5 to compile and run the app, a URL will be output to the terminal to which a POST
test request can be sent:
Postman can send a test request, passing in a test JSON payload.
If this is configured correctly, it will return 200 OK
, and the message will appear in Slack!
Build the Azure App Function
This example uses Github Actions to build the function code, package it, and push it to Octopus, which deploys it to Azure.
The build YAML can be found in .github/workflows/AzureSlackFunction.yaml
in the samples repo.
Deploy the Azure App Function
The Azure Function App created here is deployed to with Octopus, using a deployment target type of Azure web app. For more information see, deploying a package to an Azure web app.
A project has been configured to deploy the Function App.
The project has two steps:
- Deploy the Azure Function App.
- Set the environment variable for
SLACK_URI_APIKEY
.
The project can be viewed in the AzFuncNotifySlack
project on our Octopus samples instance.
Test the subscription
If an Octopus user is changed, the change is shown in the audit trail.
And the message is sent from the subscription webhook to the Azure Function App in Azure and then on to Slack.
Help us continuously improve
Please let us know if you have any feedback about this page.
Page updated on Sunday, January 1, 2023