Introduction
In this article, we will see how we can trigger Azure function and make third-party HTTP post-calls from an Azure function.
Scenario
Nowadays, IT companies are using GitHub for maintaining repositories, logging bugs etc. Many times, developers ignore the logged bug, now in order to escalate or alert every stack holder of that repository, managers prefer to create a group and post the same in that group in order to keep every stack holder on the same page. But for this, someone has to post the newly created bug into that group. Let's automate this process using an Azure function.
Steps
- Register at slack.com
- Create Slack App
- Configure incoming webhook in slack
- Verify Slack incoming webhook through Postman
- Create Azure function through the portal
- Run the function and verify in Slack
- Add webhook in GitHub
- Verify end to end flow
Register at slack.com
Register in slack if it's not already been done by browsing the below URL.
https://slack.com/signin
Click on the link 'Create a new workspace'.
Enter your valid email address and click on the 'Next' button.
Get the confirmation code from your given mail and enter as below.
Provide your full name and click on 'Continue to Password'.
Enter the password which you can easily remember and click on 'Continue to Company Name'.
Enter company or group name and click on 'Continue to Workspace URL'.
Finally, provide the workspace name to create a workspace and click on 'Create Workspace'.
Click on 'I Agree' link.
Your workspace is created in Slack.
Create Slack App
Browse the below URL.
https://api.slack.com/apps?new_app=1
In the second step, we need to create a Slack App. As we are going to use to track issues, the name is given like 'CurrentIssues'.
Select the workspace and click on 'Create App' button.
Configure incoming webhook in slack
Under the CurrentIssues app, we need to create Incoming Webhooks which is used to post messages from external resources into slack.
Click on 'Basic Information' from the left panel.
Click on 'Incoming Webhooks' in the right panel.
Enable 'Activate Incoming Webhooks' through the slider.
Click on 'Add New Webhook to Workspace' button.
Select the channel where you want to post the issues and click on 'Authorize' button.
Incoming Webhook URL is created, now you can copy the same by clicking on 'Copy' button.
Verify Slack incoming webhook through Postman
Open the Postman app.
Select 'POST' as Method.
Paste the previously copied URL.
Select the body tab and click on 'raw' radio button.
Add the body part in the multiline text box in JSON format, here we have added "Hello World" for testing purpose.
Click on 'Send' button.
We have posted a message from postman to slack. Let's verify the same.
Browse the below URL to check the message.
https://akshayblevel.slack.com/team/akshayblevel
We can see that "Hello World" message is added in the 'general' channel of slack.
Create Azure function through the portal
Let's create function app which will post the current issue details to slack and finally this function app will be triggered by git hub as soon as the bug is logged.
Before we move ahead, we need to have a basic understanding of Function App:
Azure Function is a solution where we want to execute a small piece of code or function in the cloud. We can trigger this function using an HTTP request. It provides facility to develop a serverless application in the cloud.
Click on 'Create a resource' link from the left panel.
Click on 'Compute' and 'Function App' from the right panel.
Give the proper name for the app.
Select .NET as runtime stack and keep the rest of the selection as it is.
Click on 'Create' button.
Function App is created and it shows the status as Running. Click on 'New Function' to add function,
Select the 'In-Portal' as the development environment.
Select 'Webhook + API' function which will be run whenever it receives an HTTP request.
Click on 'Create' button which will create a function with a basic template.
Add the below code snippet to the function that contains the Newtonsoft.Json reference to deserialize the request body.
Create SendSlackMessage method and call it from the main function.
SendSlackMessage generates the request body and call the slack Incoming Webhook URL to post the message to slack
#r "Newtonsoft.Json"
- using System.Net;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.Extensions.Primitives;
- using Newtonsoft.Json;
- using System.Text;
-
- public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
- {
- log.LogInformation("C# HTTP trigger function processed a request.");
-
- string issue = req.Query["text"];
-
- string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
- dynamic data = JsonConvert.DeserializeObject(requestBody);
- issue = issue ?? data?.text;
-
- if(issue != null)
- {
- var results = await SendSlackMessage(issue);
- return (ActionResult)new OkObjectResult($"Issue posted to Slack successfully.");
- }
- else
- {
- return new BadRequestObjectResult("Please pass a issue on the query string or in the request body");
- }
- }
-
- public static async Task<string> SendSlackMessage(string text)
- {
- using (var client = new HttpClient())
- {
-
- Dictionary<string, string> dictionary = new Dictionary<string, string>();
- dictionary.Add("text", text);
-
- string json = JsonConvert.SerializeObject(dictionary);
-
- var requestData = new StringContent(json, Encoding.UTF8, "application/json");
-
- var response = await client.PostAsync(String.Format("https://hooks.slack.com/services/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"), requestData);
- var result = await response.Content.ReadAsStringAsync();
-
- return result;
- }
- }
Run the function and verify in Slack
Click on 'Run' link.
In the log, we can observe that the function is compiled and executed successfully.
We can also check the output in the Test tab.
We can see the message posted from Azure function to Slack.
Click on 'Get Function URL' under newly created function,
Copy the URL which will be used later on while configuring on GitHub.
Add webhook in GitHub
Log in into Github and click on any of the existing repositories.
Click on the Setting tab.
Click on 'Webhooks' from the left bar.
Click on 'Add webhook' button.
Paste the previously copied function URL in the Payload URL text box.
Check the 'Issues' checkbox.
Click on 'Add webhook' button which will create a webhook.
Find the below sample payload; i.e., Github issue information, and we can send any details from the below to Slack.
Issue Event Payload
- {
- "action": "edited",
- "issue": {
- "url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2",
- "repository_url": "https://api.github.com/repos/Codertocat/Hello-World",
- "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2/labels{/name}",
- "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2/comments",
- "events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2/events",
- "html_url": "https://github.com/Codertocat/Hello-World/issues/2",
- "id": 327883527,
- "node_id": "MDU6SXNzdWUzMjc4ODM1Mjc=",
- "number": 2,
- "title": "Spelling error in the README file",
- "user": {
- "login": "Codertocat",
- "id": 21031067,
- "node_id": "MDQ6VXNlcjIxMDMxMDY3",
- "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/Codertocat",
- "html_url": "https://github.com/Codertocat",
- "followers_url": "https://api.github.com/users/Codertocat/followers",
- "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
- "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
- "organizations_url": "https://api.github.com/users/Codertocat/orgs",
- "repos_url": "https://api.github.com/users/Codertocat/repos",
- "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Codertocat/received_events",
- "type": "User",
- "site_admin": false
- },
- "labels": [
- {
- "id": 949737505,
- "node_id": "MDU6TGFiZWw5NDk3Mzc1MDU=",
- "url": "https://api.github.com/repos/Codertocat/Hello-World/labels/bug",
- "name": "bug",
- "color": "d73a4a",
- "default": true
- }
- ],
- "state": "open",
- "locked": false,
- "assignee": null,
- "assignees": [
-
- ],
- "milestone": null,
- "comments": 0,
- "created_at": "2018-05-30T20:18:32Z",
- "updated_at": "2018-05-30T20:18:32Z",
- "closed_at": null,
- "author_association": "OWNER",
- "body": "It looks like you accidently spelled 'commit' with two 't's."
- },
- "changes": {
- },
- "repository": {
- "id": 135493233,
- "node_id": "MDEwOlJlcG9zaXRvcnkxMzU0OTMyMzM=",
- "name": "Hello-World",
- "full_name": "Codertocat/Hello-World",
- "owner": {
- "login": "Codertocat",
- "id": 21031067,
- "node_id": "MDQ6VXNlcjIxMDMxMDY3",
- "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/Codertocat",
- "html_url": "https://github.com/Codertocat",
- "followers_url": "https://api.github.com/users/Codertocat/followers",
- "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
- "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
- "organizations_url": "https://api.github.com/users/Codertocat/orgs",
- "repos_url": "https://api.github.com/users/Codertocat/repos",
- "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Codertocat/received_events",
- "type": "User",
- "site_admin": false
- },
- "private": false,
- "html_url": "https://github.com/Codertocat/Hello-World",
- "description": null,
- "fork": false,
- "url": "https://api.github.com/repos/Codertocat/Hello-World",
- "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks",
- "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams",
- "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks",
- "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}",
- "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events",
- "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}",
- "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}",
- "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags",
- "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages",
- "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers",
- "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors",
- "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers",
- "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription",
- "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}",
- "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges",
- "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads",
- "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}",
- "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}",
- "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}",
- "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments",
- "created_at": "2018-05-30T20:18:04Z",
- "updated_at": "2018-05-30T20:18:10Z",
- "pushed_at": "2018-05-30T20:18:30Z",
- "git_url": "git://github.com/Codertocat/Hello-World.git",
- "ssh_url": "[email protected]:Codertocat/Hello-World.git",
- "clone_url": "https://github.com/Codertocat/Hello-World.git",
- "svn_url": "https://github.com/Codertocat/Hello-World",
- "homepage": null,
- "size": 0,
- "stargazers_count": 0,
- "watchers_count": 0,
- "language": null,
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": true,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "open_issues_count": 2,
- "license": null,
- "forks": 0,
- "open_issues": 2,
- "watchers": 0,
- "default_branch": "master"
- },
- "sender": {
- "login": "Codertocat",
- "id": 21031067,
- "node_id": "MDQ6VXNlcjIxMDMxMDY3",
- "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/Codertocat",
- "html_url": "https://github.com/Codertocat",
- "followers_url": "https://api.github.com/users/Codertocat/followers",
- "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
- "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
- "organizations_url": "https://api.github.com/users/Codertocat/orgs",
- "repos_url": "https://api.github.com/users/Codertocat/repos",
- "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Codertocat/received_events",
- "type": "User",
- "site_admin": false
- }
- }
For testing purposes, we are going to send issue title only.
Modify the function code as below, we need to get request body from git hub webhook.
Verify end to end flow
Let's verify the end to end flow, i.e., create an issue on GitHub and verify it in the Slack channel.
Click on the Issues tab inside the GitHub repository. Click on 'New issue' button and provide the required details; e.g., title.
We can see that recently created issue detail, i.e., the title, is posted under the Slack channel.
References:
- https://functions.azure.com
- https://slack.com
- https://github.com/