Kennisportal
Kennisportal is een kennisplatform met een focus op de brede doelgroep Business en IT.

Consuming Webhooks with Logic Apps

Scenario

The scenario covers a situation where you manage a public GitHub repo, but your real development happens in Visual Studio Team Services.  In such a case, you want every GitHub issue that gets created to be automatically placed on the VSTS backlog.  Let’s have a look!

Logic Apps has an extremely handy webhook trigger.  The trigger can be configured, so it registers the webhook when the Logic App gets enabled and that deregistration occurs on a Logic App disable.  These are the necessary steps to setup this devops integration scenario.

Register and deregister the webhook

  • Create a Logic App with the webhook trigger.
  • Configure the trigger properties to register the webhook:
  • Configure the trigger properties to un-register the webhook:
    • Unsubscribe Method: DELETE
    • Unsubscribe URI: compose the URI with the returned GitHub webhook id, via this expression:
      @{concat(‘https://api.github.com/repos/{userName}/{repoName}/hooks/’, triggerOutputs().subscribe.body.id)}

  • Both operations must use Basic Authentication to authenticate against the GitHub API.  The password should be retrieved from Key Vault, at deploy time.

Validate hash-based message authentication code

It’s advised to validate the HMAC code, that is provided in the X-Hub-Signature header, as explained over here.  In order to achieve this, we’ll introduce an Azure Function. The basis for this function is taken from this blog.

  • Create an Azure Function “ValidateRequest” with this logic:
#r “System.Security”
using System.Net;
using System.Text;
using System.Security.Cryptography;
 
private const string Sha1Prefix = sha1=;
 
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info(C# HTTP trigger function processed a request.);
 
string signatureWithPrefix = req.Headers.GetValues(X-Hub-Signature).FirstOrDefault();
log.Info(Signature: + signatureWithPrefix);
 
string sharedSecret = req.Headers.GetValues(X-LogicApp-Secret).FirstOrDefault();
log.Info(Secret: + sharedSecret);
 
var payload = await req.Content.ReadAsStringAsync();
log.Info(Payload: + payload);
 
bool isValid = IsGithubPushAllowed(payload, signatureWithPrefix, sharedSecret);
 
return isValid == false
? req.CreateResponse(HttpStatusCode.BadRequest, Missing or invalid Github security headers!)
: req.CreateResponse(HttpStatusCode.OK);
}
 
public static bool IsGithubPushAllowed(string payload, string signatureWithPrefix, string sharedSecret)
{
if (string.IsNullOrWhiteSpace(payload))
{
throw new ArgumentNullException(nameof(payload));
}
 
if (string.IsNullOrWhiteSpace(signatureWithPrefix))
{
throw new ArgumentNullException(nameof(signatureWithPrefix));
}
 
if (signatureWithPrefix.StartsWith(Sha1Prefix, StringComparison.OrdinalIgnoreCase))
{
var signature = signatureWithPrefix.Substring(Sha1Prefix.Length);
var secret = Encoding.ASCII.GetBytes(sharedSecret);
var payloadBytes = Encoding.ASCII.GetBytes(payload);
using (var hmSha1 = new HMACSHA1(secret))
{
var hash = hmSha1.ComputeHash(payloadBytes);
var hashString = ToHexString(hash);
if (hashString.Equals(signature))
{
return true;
}
}
}
 
return false;
}
 
public static string ToHexString(byte[] bytes)
{
var builder = new StringBuilder(bytes.Length * 2);
foreach (byte b in bytes)
{
builder.AppendFormat({0:x2}, b);
}
return builder.ToString();
}
view raw ValidateHmac.cs hosted with ❤ by GitHub
  • Call this Azure Function from within the Logic App:
    • Pass the complete request body
    • Pass the HTTP headers and add the “X-LogicApp-Secret” header via this expression:
      @addProperty(triggerOutputs()[‘headers’], ‘X-LogicApp-Secret’, ‘ThisIsMySecret’)

Handle the GitHub webhook

  • Use the Parse JSON action to be able to easily access the event data in the next actions.  Use a sample request to generate the JSON schema.

  • GitHub sends each 5 minutes a ping request.  Implement some decision logic to cancel the workflow in case we receive such a ping request.  This can be verified through the X-GitHub-Event HTTP header.

  • Another decision we need is to only continue processing in the case it’s a new issue that was created.  This can be determined through the action, which should be equal to opened.

Create the VSTS work item

  • Configure the “Create a work item” action, including the title and description taken from the GitHub event.

Testing

  • When we create an issue in GitHub:

  • The Logic App gets immediately fired:

  • And a VSTS work item is created:

Conclusion

Let’s see whether we fulfilled all responsibilities for webhook consumers:

  • Availability: is native to serverless technology
  • Scalability: is native to serverless technology, we can add throttling via concurrency control

  • Reliability: Logic Apps automatically persists the message.  Retry policies can be configured towards the backend system:

  • Security: HMAC code has been verified through Azure Function
  • Sequencing: not really needed, only new issues are taken into account

Logic Apps provides seamless and incredibly easy integration for webhooks!