This is a post part of a series of implementing serverless approval workflows on Azure:
- Approval Workflow on Logic Apps with Twilio
- Approval Workflow on Azure Durable Functions and SendGrid
- Approval Workflow on Azure Durable Functions and Slack (this)
Recently, I published a post about implementing an Approval Workflow on Azure Durable Functions with SendGrid. In essence, this post is not very different to that one. However, I wanted to demonstrate the same pattern on Azure Durable Functions, but now using Slack as a means of approval. My aim is to show how easy it is to implement this pattern by using a Restful API instead of an Azure Functions binding. What you see here could easily be implemented with your own custom APIs as well :).
In my previous post, I show how Furry Models Australia streamlined an approval process for aspiring cats to join the exclusive model agency by implementing a serverless solution on Azure Durable Functions and SendGrid. Now, after a great success, they’ve launched a new campaign targeting rabbits. However, for this campaign they need some customisation. The (rabbit) managers of this campaign have started to collaborate internally with Slack instead of email. Their aim is to significantly improve their current approval process based on phone and pigeon post by having an automated serverless workflow which leverages Slack as their internal messaging platform.
To build this solution, we need:
- Workspace: In case you don’t have one, you would need to create a workspace on Slack, and you will need permissions to manage apps in the workspace.
- Channel: On that workspace, you need to create a channel where all approval requests will be sent to.
- App: Once you have admin privileges on your Slack workspace, you should create a Slack App.
- Incoming Webhook: On your Slack app, you would need to activate incoming webhooks and then activate a new webhook. The incoming webhook will post messages to the channel you have just created. For that, you must authorise the app to post messages to the channel. Once you have authorised it, you should be able to get the Webhook URL. You will need this URL to configure your Durable Function to post an approval request message every time an application has been received.
- Message Template: To be able to send interactive button messages to Slack we need to have the appropriate message template.
- Interactive Components: The webhook configured above enables you to post messages to Slack. Now you need a way to get the response from Slack, for this you can use interactive message buttons. To configure the interactive message button, you must provide a request URL. This request URL will be the URL of the HttpTrigger Azure function that will handle the approval selection.
- Azure Storage Account: The solution requires a Storage Account with 3 blob containers: requests, approved, and rejected. The requests container should have public access level so blobs can be viewed without a SAS token. For your own solution, you could make this more secure.
The figure bellow, shows an overview of the solution we will build based on Durable Functions. As you can see, the workflow is very similar to the one implemented previously. Pictures of the aspiring rabbits are to be dropped in an Azure storage account blob container called requests. At the end of the approval workflow, pictures should be moved to the approved or rejected blob containers accordingly.
The steps of the process are described as follows:
- The process is being triggered by an Azure Function with the BlobTrigger input binding monitoring the requests blob container. This function also implements the DurableOrchestrationClient attribute to instantiate a Durable Function orchestration
- The DurableOrchestrationClient starts the orchestration.
- Then, the Durable Function orchestration calls another function with the ActivityTrigger input binding, which is in charge of sending the approval request to Slack as a Slack interactive message.
- The interactive message is posted on Slack. This interactive message includes a callbackId field in which we send the orchestration instance id.
- Then, in the orchestration, a timer is created so that the approval workflow does not run forever, and in case no approval is received before a timeout, the request is rejected.
- The (rabbit) user receives the interactive message on Slack, and decides whether the aspiring rabbit deserves to join Furry Models, by clicking either the Approve or Reject button. The slack interactive message button will send the response to the configured URL on the Interactive Component of the Slack App (this is the URL of the HttpTrigger function which handles the Slack approval response). The response contains the callbackId field which will allow the correlation in the next step.
- The HttpTrigger function receives the response which contains the selection and the callbackId. This function gets the orchestration instance id from the callbackId and checks the status of that instance; if it’s not running, it returns an error message to the user. If it’s running, it raises an event to the corresponding orchestration instance.
- The corresponding orchestration instance receives the external event.
- The workflow continues when the external event is received or when the timer finishes; whatever happens first. If the timer finishes before a selection is received, the application is automatically rejected.
- The orchestration calls another ActivityTrigger function to move the blob to the corresponding container (approved or rejected).
- The orchestration finishes.
A sample of the Slack interactive message is shown below.
Then, when the user clicks on any of the buttons, it will call the HttpTrigger function described in the step 7 above. Depending on the selection and the status of the orchestration, it will receive the corresponding response:
The implemented solution code can be found in this GitHub repo. I’ve used the Azure Functions Runtime v2. I will highlight some relevant bits of the code below, and I hope that the code is self-explanatory 😉:
This BlobTrigger function is triggered when a blob is created in a blob container and starts the Durable Function ochestration (Step 1 above)
This is the Durable Function orchestration which handles the workflow and is started by the step 2 above.
ActivityTrigger function which sends the approval request via Slack as an Interactive Message (Step 3 above).
HttpTrigger function that handles the response of the interactive messages from Slack (Step 7 above).
ActivityTrigger function that moves the blob to the corresponding container (Step 10 above).
These are the settings which configure the behaviour of the solution, including the storage account connection strings, the Slack incoming webhook URL, templates for the interactive message, among others.
You would need to implement these as app settings when deploying to Azure
In this post, I’ve shown how to implement an Approval Workflow (Human Interaction pattern) on Azure Durable Functions with Slack. On the way, we've also seen how to create Slack Apps with interactive messages. What you read here can easily be implemented using your own custom APIs. What we've covered should allow you to build serverless approval workflows on Azure with different means of approval. I hope you’ve found the posts of this series useful. 😊