DEV Community

loading...
Cover image for Azure Function Github to Slack Pipeline

Azure Function Github to Slack Pipeline

moe23 profile image Mohamad Lawand Updated on ・10 min read

In this post I will be showing you today How to implement a Github to Slack pipeline with Azure functions

We will cover Azure function fundamentals, and will implement a function which will send a message to a slack channel when we commit a change to our GitHub repository.

We will also be utilising Webhooks to receive a message to our azure function and a Webhook to send to slack.

You can also watch the full step by step video on YouTube:

Some of the topics that we will covering today are: whats serverless, whats an azure function, benefits of azure functions and Webhooks.

Before we start let's cover what is an Azure function. Azure functions is a serverless application platform it provide a simple way to run small programs or functions in the cloud. it could be referred to as (FaaS) function as a service platform.

So what does the word serverless means, in typical hosting scenarios we will have a server Windows or Linux which will be hosting our applications. And we will be responsible for managing that server and its resources, making sure it always up-to-date and has all of its security fixes ... In as serverless we delegate this task to the cloud, we don't care about the servers and its configurations and how it works as the cloud provider in our case Azure will be responsible for it. All we need to do is upload our code and thats it

Some of the benefits of running a serverless application

  • Automatic scaling of our application
  • flexible payment, were we are only charged when its running
  • it can handle different type of coding languages C#, Java Java Script, Python, Powershell
  • managed by the azure portal or the CLI

Before we jump into development we will need to have an Azure account, the registration processes is straight forward, all you need to do is visit https://azure.microsoft.com and create your account. You will need to provide a credit card but all of the services we are going to be using is free.

As well we will need to have a GitHub account which is free.

You will also need a slack account which is free.

So before we start we need to download the tools that we need to use Azure functions, will navigate to Microsoft documentation https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=macos%2Ccsharp%2Cbash and since i am on a Mac i will be using homebrew to install the Azure functions CLI, we will be focusing on used v3 of the Azure Functions

brew tap azure/functions
brew install azure-functions-core-tools@3
# if upgrading on a machine that has 2.x installed
brew link --overwrite azure-functions-core-tools@3
Enter fullscreen mode Exit fullscreen mode

Now as well lets install an extension to visual studio code.

Now let's do some of the slack configuration that needs to be done before jumping into the code.

Lets open slack, and create a channel inside slack call it "Azure Functions"

Once the channel is created we need to click on "Add App" and from the provided list we need to select incoming webhooks

Once you complete the Webhook installation you will get a Webhook URL which we need to save so we can use it

https://hooks.slack.com/services/xxxxxxx/xxxxxxxxx/xxxxx1iaQ4S2KvBbGdB

The next step is to test if everything is working properly, let's open Postman and try to send a request and see what happens. The request needs to be a POST and the body of the request should be as follow:

{
    "text" : "Hi from postman"
}
Enter fullscreen mode Exit fullscreen mode

Let now open our Azure portal, and click on

  • Create new resource
  • Search for function app
  • Select function app from the list
  • Click on create

Now we have a form that we need to fill to configure our function app

  • Select the subscription that you want for me it will be pay as you go
  • For resource group create a new once so it will be easily deleted at the end so you don't encore any charges
  • Add a name for our application i am going to call "YoutubeProject"
  • Runtime stack we will be choosing .net core
  • Region i will be choosing uk south
  • click on next "Hosting"
  • We will leave the storage account as is
  • Operating system "Windows"
  • Plan "Consumption"
  • click on Monitoring
  • will leave everything as is
  • click on tags we don't need to change anything there
  • click on Review + Create and will click on create

Creating the function will take a few minutes, once its finish lets go to our newly created resources and check if everything is there.

Now lets jump back into our terminal

Lets create our http function using our CLI, inside our terminal

func init YoutubeProject
Enter fullscreen mode Exit fullscreen mode

we select dotnet by pressing 1, after that we navigate to the folder and type the below

func new 
Enter fullscreen mode Exit fullscreen mode

for HttpTrigger select 2 and then lets give it a name "GithubSlackPipeline"

now lets open our code in VS code and update the function to the below. All of the code is explain in the comments above every line. Will add a new function which will be responsible for connecting to Slack

// Slack hook
public static async Task<string> SendSlackMessage(string message)
{
    // Update this webhook to your own one
    var slackWebhookUrl = "https://hooks.slack.com/services/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";

    // Using the using will automatically call the garbage collector and clean 
    using(var client  = new HttpClient())
    {
        // Building the Json body
        var jsonBody = "{'text': '" + message + "'}";
        var data = new StringContent(jsonBody, Encoding.UTF8, "application/json");

        var response = await client.PostAsync(slackWebhookUrl, data);

        var result = await response.Content.ReadAsStringAsync();

        return result;
    }
}
Enter fullscreen mode Exit fullscreen mode

The next thing to do is to add the call to this function in our Main function

await SendSlackMessage("Test from az func");
Enter fullscreen mode Exit fullscreen mode

Lets now run the application and test it locally and see if its working

func start --build
Enter fullscreen mode Exit fullscreen mode

After the func start has been completed we can see a url has been created for us, lets copy this url to our web browser to test it will need to give it a query string name as it is the default behaviour thats generated by the out of the box template we will be removing this later.

 // http://localhost:7071/api/slackhook?name=mohamad
Enter fullscreen mode Exit fullscreen mode

Once we run it in the browser we can see we got a message in slack which means our code is working.

Lets try an deploy this to Azure and see if we can run it from a live environment. Using VS code we are going to install an extension called Azure functions.

Alt Text

And then we will click on its icon and then the upload button on the top

Alt Text

Azure function extension

Alt Text

Upload button

Once we click on upload will follow the wizard to upload the function.

Then will navigate to Azure Portal ⇒ Resources ⇒ Youtube ⇒ Azure functions ⇒ YoutubeProject ⇒ Functions ⇒ GithubSlackPipeline

Alt Text

and will copy this url into the browser and test it with the name.

https://xxxxxxxxxx.azurewebsites.net/api/slackhook?code=xxxxxxxxxxxxxxx//xxxxxx/8Emn/AAQ==?name=mohamad

Now we lets check slack, we can see we are getting a message.

Now that we have our slack part configured and completed in our function its Github time, the first thing we will be doing is looking at the Webhooks documentation for push and see whats the objects that is being sent.

lets go to this link: https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#push and we can check the payload that this Webhook produce.

As we can see its a Json payload, so lets convert it to C# and add it to our function, to do the conversion we will be using a service called https://jsonutils.com which a great free service.

we paste the json into the box and click on submit and within seconds will get our call. We can see that the result is composed of multiple classes. Let's copy this classes and add it to our solution. Will create a folder called Models and inside will create a class called GithubHook

public class Owner
    {
        public string name { get; set; }
        public string email { get; set; }
        public string login { get; set; }
        public int id { get; set; }
        public string node_id { get; set; }
        public string avatar_url { get; set; }
        public string gravatar_id { get; set; }
        public string url { get; set; }
        public string html_url { get; set; }
        public string followers_url { get; set; }
        public string following_url { get; set; }
        public string gists_url { get; set; }
        public string starred_url { get; set; }
        public string subscriptions_url { get; set; }
        public string organizations_url { get; set; }
        public string repos_url { get; set; }
        public string events_url { get; set; }
        public string received_events_url { get; set; }
        public string type { get; set; }
        public bool site_admin { get; set; }
    }

    public class License
    {
        public string key { get; set; }
        public string name { get; set; }
        public string spdx_id { get; set; }
        public string url { get; set; }
        public string node_id { get; set; }
    }

    public class Repository
    {
        public int id { get; set; }
        public string node_id { get; set; }
        public string name { get; set; }
        public string full_name { get; set; }
        //public bool private { get; set; }
        public Owner owner { get; set; }
        public string html_url { get; set; }
        public string description { get; set; }
        public bool fork { get; set; }
        public string url { get; set; }
        public string forks_url { get; set; }
        public string keys_url { get; set; }
        public string collaborators_url { get; set; }
        public string teams_url { get; set; }
        public string hooks_url { get; set; }
        public string issue_events_url { get; set; }
        public string events_url { get; set; }
        public string assignees_url { get; set; }
        public string branches_url { get; set; }
        public string tags_url { get; set; }
        public string blobs_url { get; set; }
        public string git_tags_url { get; set; }
        public string git_refs_url { get; set; }
        public string trees_url { get; set; }
        public string statuses_url { get; set; }
        public string languages_url { get; set; }
        public string stargazers_url { get; set; }
        public string contributors_url { get; set; }
        public string subscribers_url { get; set; }
        public string subscription_url { get; set; }
        public string commits_url { get; set; }
        public string git_commits_url { get; set; }
        public string comments_url { get; set; }
        public string issue_comment_url { get; set; }
        public string contents_url { get; set; }
        public string compare_url { get; set; }
        public string merges_url { get; set; }
        public string archive_url { get; set; }
        public string downloads_url { get; set; }
        public string issues_url { get; set; }
        public string pulls_url { get; set; }
        public string milestones_url { get; set; }
        public string notifications_url { get; set; }
        public string labels_url { get; set; }
        public string releases_url { get; set; }
        public string deployments_url { get; set; }
        public int created_at { get; set; }
        public DateTime updated_at { get; set; }
        public int pushed_at { get; set; }
        public string git_url { get; set; }
        public string ssh_url { get; set; }
        public string clone_url { get; set; }
        public string svn_url { get; set; }
        public object homepage { get; set; }
        public int size { get; set; }
        public int stargazers_count { get; set; }
        public int watchers_count { get; set; }
        public string language { get; set; }
        public bool has_issues { get; set; }
        public bool has_projects { get; set; }
        public bool has_downloads { get; set; }
        public bool has_wiki { get; set; }
        public bool has_pages { get; set; }
        public int forks_count { get; set; }
        public object mirror_url { get; set; }
        public bool archived { get; set; }
        public bool disabled { get; set; }
        public int open_issues_count { get; set; }
        public License license { get; set; }
        public int forks { get; set; }
        public int open_issues { get; set; }
        public int watchers { get; set; }
        public string default_branch { get; set; }
        public int stargazers { get; set; }
        public string master_branch { get; set; }
    }

    public class Pusher
    {
        public string name { get; set; }
        public string email { get; set; }
    }

    public class Sender
    {
        public string login { get; set; }
        public int id { get; set; }
        public string node_id { get; set; }
        public string avatar_url { get; set; }
        public string gravatar_id { get; set; }
        public string url { get; set; }
        public string html_url { get; set; }
        public string followers_url { get; set; }
        public string following_url { get; set; }
        public string gists_url { get; set; }
        public string starred_url { get; set; }
        public string subscriptions_url { get; set; }
        public string organizations_url { get; set; }
        public string repos_url { get; set; }
        public string events_url { get; set; }
        public string received_events_url { get; set; }
        public string type { get; set; }
        public bool site_admin { get; set; }
    }

    public class Author
    {
        public string name { get; set; }
        public string email { get; set; }
        public string username { get; set; }
    }

    public class Committer
    {
        public string name { get; set; }
        public string email { get; set; }
        public string username { get; set; }
    }

    public class Commit
    {
        public string id { get; set; }
        public string tree_id { get; set; }
        public bool distinct { get; set; }
        public string message { get; set; }
        public DateTime timestamp { get; set; }
        public string url { get; set; }
        public Author author { get; set; }
        public Committer committer { get; set; }
        public IList<string> added { get; set; }
        public IList<object> removed { get; set; }
        public IList<string> modified { get; set; }
    }

    public class HeadCommit
    {
        public string id { get; set; }
        public string tree_id { get; set; }
        public bool distinct { get; set; }
        public string message { get; set; }
        public DateTime timestamp { get; set; }
        public string url { get; set; }
        //public  author { get; set; }
        //public  committer { get; set; }
        public IList<string> added { get; set; }
        public IList<object> removed { get; set; }
        public IList<string> modified { get; set; }
    }

    public class GithubHook
    {
        //public string ref { get; set; }
        public string before { get; set; }
        public string after { get; set; }
        public Repository repository { get; set; }
        public Pusher pusher { get; set; }
        public Sender sender { get; set; }
        public bool created { get; set; }
        public bool deleted { get; set; }
        public bool forced { get; set; }
        public object base_ref { get; set; }
        public string compare { get; set; }
        public IList<Commit> commits { get; set; }
        public HeadCommit head_commit { get; set; }
    }
Enter fullscreen mode Exit fullscreen mode

So in order for us to Github Webhook we need to have a repository and once we navigate to it we need to go to

Select repository ⇒ Settings ⇒ Webhooks ⇒ Add Webhook

We will need to add the below configs

  • Get Azure function Url and past it in the Payload URL
  • Select application/json in the Content-Type
  • Click on add Webhook

Next we will need to update our Azure function to use the payload, serialise it an upload it to push a message to slack

public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
{
    log.LogInformation("Github-Slack pipeline has been initiated.");

    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    var data = JsonConvert.DeserializeObject<GithubHook>(requestBody);

    var msg= $"New commit has been pushed. Id: {data.commits[0].id} Message: {data.commits[0].message}";

    // Trigger the Slack webhook
    await SendSlackMessage(msg);

    string commitList = "";

    foreach(var commit in data?.commits)
    {
        commitList += $"Commit Id: {commit.id} \n";
    }

    string responseMessage = $"Hello, these commits {commitList} has been pushed.";
    log.LogInformation("Github-Slack pipeline has been complete successfully.");

    return new OkObjectResult(responseMessage);
}
Enter fullscreen mode Exit fullscreen mode

Notes:

An Azure function are put in an Azure function apps, where we can have multiple functions running next to each other

So what are functions triggers: basically its , there is many types of triggers but we will be focusing on 2 types of triggers

  • Http triggers ⇒ Webhooks or create Rest API using Azure functions

When using a HttpTrigger Azure functions will allow to customise the following

  • HTTP method do we want to respond to Get/Post
  • Route on which we want to function to respond
  • Security level using authorisation keys, there is 3 level for these keys:
    • Anonymous: no key is provided
    • Function level: every call must have a key which specific to this function
    • Admin level: key can work with any function inside our function app

Discussion (0)

pic
Editor guide