Azure Serverless Functions With Real World Scenario

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.

Azure Serverless Functions With Real World Scenario 

Steps

  1. Register at slack.com
  2. Create Slack App
  3. Configure incoming webhook in slack
  4. Verify Slack incoming webhook through Postman
  5. Create Azure function through the portal
  6. Run the function and verify in Slack
  7. Add webhook in GitHub
  8. 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'.

Azure Serverless Functions With Real World Scenario 

Enter your valid email address and click on the 'Next' button.

Azure Serverless Functions With Real World Scenario 

Get the confirmation code from your given mail and enter as below.

Azure Serverless Functions With Real World Scenario

Provide your full name and click on 'Continue to Password'.

Azure Serverless Functions With Real World Scenario

Enter the password which you can easily remember and click on 'Continue to Company Name'.

Azure Serverless Functions With Real World Scenario

Enter company or group name and click on 'Continue to Workspace URL'.

Azure Serverless Functions With Real World Scenario

Finally, provide the workspace name to create a workspace and click on 'Create Workspace'.

Azure Serverless Functions With Real World Scenario

Click on 'I Agree' link.

Azure Serverless Functions With Real World Scenario 

Your workspace is created in Slack.

Azure Serverless Functions With Real World Scenario
 
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.

Azure Serverless Functions With Real World Scenario 
 
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.

Azure Serverless Functions With Real World Scenario 

Enable 'Activate Incoming Webhooks' through the slider.

Azure Serverless Functions With Real World Scenario 

Click on 'Add New Webhook to Workspace' button.

Azure Serverless Functions With Real World Scenario 

Select the channel where you want to post the issues and click on 'Authorize' button.

Azure Serverless Functions With Real World Scenario 

Incoming Webhook URL is created, now you can copy the same by clicking on 'Copy' button.

Azure Serverless Functions With Real World Scenario
 
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.

Azure Serverless Functions With Real World Scenario 

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.

Azure Serverless Functions With Real World Scenario 
 
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.

Azure Serverless Functions With Real World Scenario 

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.

Azure Serverless Functions With Real World Scenario 

Function App is created and it shows the status as Running. Click on 'New Function' to add function,

Azure Serverless Functions With Real World Scenario 

Select the 'In-Portal' as the development environment.

Azure Serverless Functions With Real World Scenario 

Select 'Webhook + API' function which will be run whenever it receives an HTTP request.

Azure Serverless Functions With Real World Scenario 

Click on 'Create' button which will create a function with a basic template.

Azure Serverless Functions With Real World Scenario 

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"

  1. using System.Net;  
  2. using Microsoft.AspNetCore.Mvc;  
  3. using Microsoft.Extensions.Primitives;  
  4. using Newtonsoft.Json;  
  5. using System.Text;  
  6.    
  7. public static async Task<IActionResult> Run(HttpRequest req, ILogger log)  
  8. {  
  9.     log.LogInformation("C# HTTP trigger function processed a request.");  
  10.    
  11.     string issue = req.Query["text"];  
  12.    
  13.     string requestBody = await new StreamReader(req.Body).ReadToEndAsync();  
  14.     dynamic data = JsonConvert.DeserializeObject(requestBody);  
  15.     issue = issue ?? data?.text;  
  16.       
  17.     if(issue != null)  
  18.     {  
  19.         var results = await SendSlackMessage(issue);  
  20.         return (ActionResult)new OkObjectResult($"Issue posted to Slack successfully.");  
  21.     }  
  22.     else  
  23.     {  
  24.          return new BadRequestObjectResult("Please pass a issue on the query string or in the request body");  
  25.     }  
  26. }  
  27.    
  28. public static async Task<string> SendSlackMessage(string text)  
  29. {  
  30.     using (var client = new HttpClient())  
  31.     {  
  32.           
  33.         Dictionary<string, string> dictionary = new Dictionary<string, string>();  
  34.         dictionary.Add("text", text);  
  35.    
  36.         string json = JsonConvert.SerializeObject(dictionary);  
  37.    
  38.         var requestData = new StringContent(json, Encoding.UTF8, "application/json");  
  39.           
  40.             var response = await client.PostAsync(String.Format("https://hooks.slack.com/services/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"), requestData);  
  41.             var result = await response.Content.ReadAsStringAsync();  
  42.    
  43.         return result;  
  44.     }  
  45. }  
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.

Azure Serverless Functions With Real World Scenario 

We can also check the output in the Test tab.

Azure Serverless Functions With Real World Scenario 

We can see the message posted from Azure function to Slack.

Azure Serverless Functions With Real World Scenario 

Click on 'Get Function URL' under newly created function,

Azure Serverless Functions With Real World Scenario 

Copy the URL which will be used later on while configuring on GitHub.

Azure Serverless Functions With Real World Scenario
 
Add webhook in GitHub

Log in into Github and click on any of the existing repositories.

Click on the Setting tab.

Azure Serverless Functions With Real World Scenario 

Click on 'Webhooks' from the left bar.

Azure Serverless Functions With Real World Scenario 

Click on 'Add webhook' button.

Azure Serverless Functions With Real World Scenario 

Paste the previously copied function URL in the Payload URL text box.

Azure Serverless Functions With Real World Scenario 

Check the 'Issues' checkbox.

Azure Serverless Functions With Real World Scenario 

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

  1. {  
  2.   "action""edited",  
  3.   "issue": {  
  4.     "url""https://api.github.com/repos/Codertocat/Hello-World/issues/2",  
  5.     "repository_url""https://api.github.com/repos/Codertocat/Hello-World",  
  6.     "labels_url""https://api.github.com/repos/Codertocat/Hello-World/issues/2/labels{/name}",  
  7.     "comments_url""https://api.github.com/repos/Codertocat/Hello-World/issues/2/comments",  
  8.     "events_url""https://api.github.com/repos/Codertocat/Hello-World/issues/2/events",  
  9.     "html_url""https://github.com/Codertocat/Hello-World/issues/2",  
  10.     "id": 327883527,  
  11.     "node_id""MDU6SXNzdWUzMjc4ODM1Mjc=",  
  12.     "number": 2,  
  13.     "title""Spelling error in the README file",  
  14.     "user": {  
  15.       "login""Codertocat",  
  16.       "id": 21031067,  
  17.       "node_id""MDQ6VXNlcjIxMDMxMDY3",  
  18.       "avatar_url""https://avatars1.githubusercontent.com/u/21031067?v=4",  
  19.       "gravatar_id""",  
  20.       "url""https://api.github.com/users/Codertocat",  
  21.       "html_url""https://github.com/Codertocat",  
  22.       "followers_url""https://api.github.com/users/Codertocat/followers",  
  23.       "following_url""https://api.github.com/users/Codertocat/following{/other_user}",  
  24.       "gists_url""https://api.github.com/users/Codertocat/gists{/gist_id}",  
  25.       "starred_url""https://api.github.com/users/Codertocat/starred{/owner}{/repo}",  
  26.       "subscriptions_url""https://api.github.com/users/Codertocat/subscriptions",  
  27.       "organizations_url""https://api.github.com/users/Codertocat/orgs",  
  28.       "repos_url""https://api.github.com/users/Codertocat/repos",  
  29.       "events_url""https://api.github.com/users/Codertocat/events{/privacy}",  
  30.       "received_events_url""https://api.github.com/users/Codertocat/received_events",  
  31.       "type""User",  
  32.       "site_admin"false  
  33.     },  
  34.     "labels": [  
  35.       {  
  36.         "id": 949737505,  
  37.         "node_id""MDU6TGFiZWw5NDk3Mzc1MDU=",  
  38.         "url""https://api.github.com/repos/Codertocat/Hello-World/labels/bug",  
  39.         "name""bug",  
  40.         "color""d73a4a",  
  41.         "default"true  
  42.       }  
  43.     ],  
  44.     "state""open",  
  45.     "locked"false,  
  46.     "assignee"null,  
  47.     "assignees": [  
  48.    
  49.     ],  
  50.     "milestone"null,  
  51.     "comments": 0,  
  52.     "created_at""2018-05-30T20:18:32Z",  
  53.     "updated_at""2018-05-30T20:18:32Z",  
  54.     "closed_at"null,  
  55.     "author_association""OWNER",  
  56.     "body""It looks like you accidently spelled 'commit' with two 't's."  
  57.   },  
  58.   "changes": {  
  59.   },  
  60.   "repository": {  
  61.     "id": 135493233,  
  62.     "node_id""MDEwOlJlcG9zaXRvcnkxMzU0OTMyMzM=",  
  63.     "name""Hello-World",  
  64.     "full_name""Codertocat/Hello-World",  
  65.     "owner": {  
  66.       "login""Codertocat",  
  67.       "id": 21031067,  
  68.       "node_id""MDQ6VXNlcjIxMDMxMDY3",  
  69.       "avatar_url""https://avatars1.githubusercontent.com/u/21031067?v=4",  
  70.       "gravatar_id""",  
  71.       "url""https://api.github.com/users/Codertocat",  
  72.       "html_url""https://github.com/Codertocat",  
  73.       "followers_url""https://api.github.com/users/Codertocat/followers",  
  74.       "following_url""https://api.github.com/users/Codertocat/following{/other_user}",  
  75.       "gists_url""https://api.github.com/users/Codertocat/gists{/gist_id}",  
  76.       "starred_url""https://api.github.com/users/Codertocat/starred{/owner}{/repo}",  
  77.       "subscriptions_url""https://api.github.com/users/Codertocat/subscriptions",  
  78.       "organizations_url""https://api.github.com/users/Codertocat/orgs",  
  79.       "repos_url""https://api.github.com/users/Codertocat/repos",  
  80.       "events_url""https://api.github.com/users/Codertocat/events{/privacy}",  
  81.       "received_events_url""https://api.github.com/users/Codertocat/received_events",  
  82.       "type""User",  
  83.       "site_admin"false  
  84.     },  
  85.     "private"false,  
  86.     "html_url""https://github.com/Codertocat/Hello-World",  
  87.     "description"null,  
  88.     "fork"false,  
  89.     "url""https://api.github.com/repos/Codertocat/Hello-World",  
  90.     "forks_url""https://api.github.com/repos/Codertocat/Hello-World/forks",  
  91.     "keys_url""https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}",  
  92.     "collaborators_url""https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}",  
  93.     "teams_url""https://api.github.com/repos/Codertocat/Hello-World/teams",  
  94.     "hooks_url""https://api.github.com/repos/Codertocat/Hello-World/hooks",  
  95.     "issue_events_url""https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}",  
  96.     "events_url""https://api.github.com/repos/Codertocat/Hello-World/events",  
  97.     "assignees_url""https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}",  
  98.     "branches_url""https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}",  
  99.     "tags_url""https://api.github.com/repos/Codertocat/Hello-World/tags",  
  100.     "blobs_url""https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}",  
  101.     "git_tags_url""https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}",  
  102.     "git_refs_url""https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}",  
  103.     "trees_url""https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}",  
  104.     "statuses_url""https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}",  
  105.     "languages_url""https://api.github.com/repos/Codertocat/Hello-World/languages",  
  106.     "stargazers_url""https://api.github.com/repos/Codertocat/Hello-World/stargazers",  
  107.     "contributors_url""https://api.github.com/repos/Codertocat/Hello-World/contributors",  
  108.     "subscribers_url""https://api.github.com/repos/Codertocat/Hello-World/subscribers",  
  109.     "subscription_url""https://api.github.com/repos/Codertocat/Hello-World/subscription",  
  110.     "commits_url""https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}",  
  111.     "git_commits_url""https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}",  
  112.     "comments_url""https://api.github.com/repos/Codertocat/Hello-World/comments{/number}",  
  113.     "issue_comment_url""https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}",  
  114.     "contents_url""https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}",  
  115.     "compare_url""https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}",  
  116.     "merges_url""https://api.github.com/repos/Codertocat/Hello-World/merges",  
  117.     "archive_url""https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}",  
  118.     "downloads_url""https://api.github.com/repos/Codertocat/Hello-World/downloads",  
  119.     "issues_url""https://api.github.com/repos/Codertocat/Hello-World/issues{/number}",  
  120.     "pulls_url""https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}",  
  121.     "milestones_url""https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}",  
  122.     "notifications_url""https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}",  
  123.     "labels_url""https://api.github.com/repos/Codertocat/Hello-World/labels{/name}",  
  124.     "releases_url""https://api.github.com/repos/Codertocat/Hello-World/releases{/id}",  
  125.     "deployments_url""https://api.github.com/repos/Codertocat/Hello-World/deployments",  
  126.     "created_at""2018-05-30T20:18:04Z",  
  127.     "updated_at""2018-05-30T20:18:10Z",  
  128.     "pushed_at""2018-05-30T20:18:30Z",  
  129.     "git_url""git://github.com/Codertocat/Hello-World.git",  
  130.     "ssh_url""[email protected]:Codertocat/Hello-World.git",  
  131.     "clone_url""https://github.com/Codertocat/Hello-World.git",  
  132.     "svn_url""https://github.com/Codertocat/Hello-World",  
  133.     "homepage"null,  
  134.     "size": 0,  
  135.     "stargazers_count": 0,  
  136.     "watchers_count": 0,  
  137.     "language"null,  
  138.     "has_issues"true,  
  139.     "has_projects"true,  
  140.     "has_downloads"true,  
  141.     "has_wiki"true,  
  142.     "has_pages"true,  
  143.     "forks_count": 0,  
  144.     "mirror_url"null,  
  145.     "archived"false,  
  146.     "open_issues_count": 2,  
  147.     "license"null,  
  148.     "forks": 0,  
  149.     "open_issues": 2,  
  150.     "watchers": 0,  
  151.     "default_branch""master"  
  152.   },  
  153.   "sender": {  
  154.     "login""Codertocat",  
  155.     "id": 21031067,  
  156.     "node_id""MDQ6VXNlcjIxMDMxMDY3",  
  157.     "avatar_url""https://avatars1.githubusercontent.com/u/21031067?v=4",  
  158.     "gravatar_id""",  
  159.     "url""https://api.github.com/users/Codertocat",  
  160.     "html_url""https://github.com/Codertocat",  
  161.     "followers_url""https://api.github.com/users/Codertocat/followers",  
  162.     "following_url""https://api.github.com/users/Codertocat/following{/other_user}",  
  163.     "gists_url""https://api.github.com/users/Codertocat/gists{/gist_id}",  
  164.     "starred_url""https://api.github.com/users/Codertocat/starred{/owner}{/repo}",  
  165.     "subscriptions_url""https://api.github.com/users/Codertocat/subscriptions",  
  166.     "organizations_url""https://api.github.com/users/Codertocat/orgs",  
  167.     "repos_url""https://api.github.com/users/Codertocat/repos",  
  168.     "events_url""https://api.github.com/users/Codertocat/events{/privacy}",  
  169.     "received_events_url""https://api.github.com/users/Codertocat/received_events",  
  170.     "type""User",  
  171.     "site_admin"false  
  172.   }  
  173. }  

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.

Azure Serverless Functions With Real World Scenario 
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.

Azure Serverless Functions With Real World Scenario 

We can see that recently created issue detail, i.e., the title, is posted under the Slack channel.

Azure Serverless Functions With Real World Scenario 

References:

  1. https://functions.azure.com
  2. https://slack.com
  3. https://github.com/