Posting Slack notifications using Google Cloud Function

Willy Camargo
Ordergroove Engineering
9 min readJun 8, 2022

A while ago here at Ordergroove, we had the need to find a reliable way of receiving messages in a Slack channel about the progress of multiple GCP Workflows. Since we didn’t find an official integration between the two services, we decided to develop our own notification system using Google Cloud Function.

In this article, we will cover how to create a simple Slack App and integrate it into an HTTP cloud function that accepts a payload with channel_id , text, button (with label + URL), and thread_ts to send notifications to Slack. I'll explain better each of the parameters further in the article.

After having this Cloud Function in place, we will be able to integrate it with any application we want, as long as the app has the ability to make HTTP requests.

Tech Stack

For this implementation, we’re going to use some GCP resources: Cloud Functions, Secret Manager, and Identity and Access Management (IAM)

1. Creating the Slack App and getting the OAuth Token

1.1. Access the Slack Apps directory and click on Create New App

1.2. On the modal that appears, select From an app manifest

1.3. Select the workspace that you wanna send the messages to and hit Next (You will need to have permissions to install Apps to this workspace)

1.4. Paste the App Manifest below to give the app permission to write at channels. You can change the app’s display name if you want, for the article we’re using the name “Notifications App”

_metadata:
major_version: 1
minor_version: 1
display_information:
name: Notifications App
features:
bot_user:
display_name: Notifications App
always_online: true
oauth_config:
scopes:
bot:
- chat:write
- chat:write.public

1.5. Navigate to the OAuth & Permissions

1.6. On the section “OAuth Tokens for Your Workspace”, click on the “Install to Workspace” button to install this app to the chosen workspace

1.7. After installing the app, you will be able to access the “Bot User OAuth Token”. We will need this token to create the app’s secret

Testing your slack app

To check if your slack app is working fine, we will post a message using the Slack API. Here’s a cURL example that you can paste on your terminal:

curl -X POST -H 'Content-type: application/json' -H 'Authorization: Bearer [slack_oauth_token]' --data '{"text":"My slack notification", "channel": "[slack_channel_id]"}' <https://slack.com/api/chat.postMessage>
  • [slack_oauth_token] use the “Bot User OAuth Token” that we created in step 7
  • [slack_channel_id] use the channel you want to send the message to. To get the slack channel id, you can right-click on the channel name and copy the link. The channel ID is at the end of the link and would be something like this “A12BC3DEF” https://ordergroove.slack.com/archives/[slack_channel_id]

2. Creating a Cloud Secret to store the Slack OAuth Token

Google Secret Manager is a secure and convenient storage system for sensitive data. Although you could use it programmatically to manage your secrets (create, update, delete, and access), we’re going to manually create one secret to store the Slack OAuth Token.

2.1. Access the GCP Console and make sure you have the correct project selected at the top left corner. If you don’t have a GCP project, you can read this official documentation to know more about it.

2.2. On the GCP Console, access the Secret Manager. (On the top search bar, type “Secret Manager” and click on the first result).

2.3. Enable the Secret Manager API (you can skip this step if you already have the Secret Manager API enabled)

2.4. Click on the “+ Create Secret” button

2.5. Fill the form with the values:
Secret Name: we will use slack-oauth-token. You can change it if you want.
Secret Value: paste the “Bot User OAuth Token” that we copied from step 7 when creating the Slack App.

3. Creating a Service Account

As you probably know, Google Cloud Platform has hundreds of products and a complex project could have dozens of resources in use. Because of that, It’s important to create an identity with the right roles for the resources we’re going to use.

For our need, we should create a service account that gives the Cloud Function the permissions to access the secrets. To do that, we should use the Identity and Access Management (IAM)

3.1. Access the IAM page. (On the top search bar, type “IAM” and click on the first result “IAM & Admin”)

3.2. On the left side navigation select “Service Accounts”

3.3. Select “Create Service Account” at the top

3.4. Give it a name and a description. We’re going to use “Slack Notification”, and hit “Create and Continue”

3.5. Grant the service account the role “Secret Manager Secret Accessor” and click “Done”

⚠️ With this role, this service account has permission to access any secret stored in this project. You could limit the service account to having access to only a specific secret by managing the roles on the same page. ⚠️

4. Creating an HTTP Cloud Function

Cloud Functions are Scalable pay-as-you-go functions as a service (FaaS) to run your code with zero server management. GCP provides us the opportunity to choose which language we want to use to write our Cloud Function code. Some of the most popular languages are available: Node.js, Python, Go, Java, .NET, Ruby, and PHP.

In our example, we will create the function using Python, but you could choose the language you prefer as far as you know how to access environment variables and make a simple request using the language.

4.1. Access the Cloud Functions page. (On the top search bar, type “Cloud Functions” and click on the first result)

4.2. Click on the “Create Function” button

4.3. Fill out the form with the “Cloud Function” information. In our example we are creating a public HTTP function, so we will use the following data:

  • Basics:
    -
    Function name: send-slack-notification
    - Region: default (in my case it is us-central1)
  • Trigger:
    -
    Click “EDIT”
    - Trigger type: HTTP
    -
    Allow unauthenticated invocations: checked
    -
    Require HTTPS: checked
    -
    Click “SAVE” — it’s important to save the trigger options
  • Runtime
    -
    Runtime service account: select the service account that we created on the preview step (slack-notification)
  • Security
    -
    Under the “Secrets” section, click on the button “REFERENCE A SECRET”
    -
    Secret: slack-oauth-token (or the name you used for the secret)
    - Reference method: Exposed as environment variable
    -
    Name 1: SLACK_OAUTH_TOKEN
    -
    Version 1: latest (this will guarantee that if you change the secret value, the function will receive the latest updated value)
  • Build: all default
  • Connections: all default

4.4. On the Code step, choose the “Python 3.9” as Runtime and change the Entry point to send_slack_notification. If the GCP show an alert asking you to enable Cloud Build API, you should allow it by clicking on the “Enable API” button

4.4. In the left panel create a file called requirements.txt with the content below:

requests==2.27.1

The requests package is the package we will use to make the requests to the Slack API.

4.5. In the main.py file, paste the code below

The code is commented on in order to explain each block of code. Feel free to read each line and remove the comments to clean it up.

After writing the code, just hit the “DEPLOY” button and if there is no error in the code, the function should be available in a couple of minutes.

5. Posting the slack message

At this point, we have everything in place. The only missing part is testing our cloud function.

Request Params

  • channel_id: Slack channel id. To access the slack channel id you can right-click on the channel name and copy the link. The channel ID is at the end of the link and would be something like this “A12BC3DEF” https://ordergroove.slack.com/archives/[slack_channel_id]
  • text: The slack message you want to send. Tip: you can use slack formatting markup to format your message. You can learn more about it at the official documentation: https://api.slack.com/reference/surfaces/formatting#basics
  • thread_ts [optional]: the thread timestamp to send this message. It’s useful to send multiple notifications related to a specific event. You can find this value as a response from any message that you send using the function
  • button [optional]: Object containing the label and url of the button that will be rendered on the right side of the message

Testing the function

5.1. Access your function page and click on the “TESTING” tab

5.2. Fill the “Triggering event” with a valid JSON payload

{
"channel_id": "[channel_id]",
"text": "Time to visit the Ordergroove Engineering Blog! 🎉",
"button": {
"label": "Click me",
"url": "https://medium.com/ordergroove-engineering"
}
}

5.3. Hit the “TEST THE FUNCTION” button and expect to receive the notification on the specified channel

5.4. The function will also return a response body that you can use to send subsequent messages as replies to the original message thread. You can use the ts value returned in this response to post the messages to the same thread

Response example:

{
"channel": "[channel_id]",
"message": {
"blocks": [
{
"accessory": {
"action_id": "dLEi",
"text": {
"emoji": true,
"text": "Click me",
"type": "plain_text"
},
"type": "button",
"url": "https://medium.com/ordergroove-engineering"
},
"block_id": "gjE",
"text": {
"text": "This is a Slack Notification",
"type": "mrkdwn",
"verbatim": false
},
"type": "section"
}
],
"bot_id": "B02QTL42YJ1",
"bot_profile": {
"app_id": "[app_id]",
"deleted": false,
"icons": {
"image_36": "https://a.slack-edge.com/80588/img/plugins/app/bot_36.png",
"image_48": "https://a.slack-edge.com/80588/img/plugins/app/bot_48.png",
"image_72": "https://a.slack-edge.com/80588/img/plugins/app/service_72.png"
},
"id": "[bot_id]",
"name": "Notifications App",
"team_id": "[team_id]",
"updated": 1639594508
},
"team": "T02CJMHATTN",
"text": "This content can't be displayed.",
"ts": "1646326518.511229",
"type": "message",
"user": "U02R97KJW9X"
},
"ok": true,
"response_metadata": {
"warnings": [
"missing_charset"
]
},
"ts": "1646326518.511229",
"warning": "missing_charset"
}

Calling the Function through an HTTP request

On a daily basis, we would need to call the function through the HTTP endpoint. In order to do that we need to access the function’s Trigger URL.

5.5. Access your function page and click on the “TRIGGER” tab. You should see the Trigger URL as the example below

5.6. You can use this Trigger URL to post a message using any client you want. Here’s an example using cURL:

curl -X POST -H 'Content-type: application/json' --data '{"text":"My slack notification", "channel": "[channel_id]"}' [trigger_url]

Conclusion

Although there are a lot of different ways of building Slack bots, having a notification app available in a request away brings many advantages.

  1. You don’t need to rely on multiple third-party Slack integrations to be notified about your services.
  2. You have full control over the integration and customization of the messages. You can style the messages, attach files, and add custom blocks.

3. You can create customized notifications and direct them to the right Slack channel. Eg:

  • Some important requests on a service failed, then sends a message to the #developers-alerts channel
  • A new customer signed up for a product, then sends a message to the #customer-creation-notification channel
  • A customer canceled a subscription, then sends a message to the #customer-churn channel

--

--