Using Azure Alert with a Function App and Azure SDK

Update: Added the code and folder structure to GitHub: https://github.com/jcbrooks92/AzureSDKFunctionAppVMCreationAlert 

 

I was helping a customer out the other day who wanted to configure a Azure Function App to pull the private IP of a newly created Azure VM to use for their backend tasks on premises. They had setup an Azure Alert which can be configured with a webhook that will send a JSON object to the webhook endpoint upon a VM creation. Here are the basics steps:

What you’ll need to familiarize your self with:

  1. An Azure Alert 
  2. Azure HTTP Triggered Function 
  3. Azure AD application to be used for authentication 
  4. Azure AD management libraries 

 

Step by step instructions:

  1. Create an Azure Function Generic webhook function targeting C#
  2. Create a project.json file via the Portal GUI under the “View files” section or in Kudu  (
    D:\home\site\wwwroot\FunctionName)

    under the root of the Function (not the function app). This will download the required dependencies for authentication from NPM
    {

    “frameworks”: {

    “net46”:{

    “dependencies”: {

    “Microsoft.Azure.Management.Compute.Fluent” : “1.3.0”,

    “Microsoft.Azure.Management.Fluent”: “1.3.0”,

    “Microsoft.Azure.Management.ResourceManager.Fluent”: “1.3.0”,

    }

    }

    }

    }

  3. Add an app setting called AZURE_AUTH_LOCATION setting the value to D:\home\site\wwwroot\WebHookVMCreation2\azureauth.propertiesThis value is pointing the code we will add later to the azureauth.properties file which will contain the Azure AD app registration information. There are other ways to do this if you review the documentation regarding the Azure AD app registration mentioned above. For production environments its probably recommended to put all the values in app settings and use the section that talks about Authenticate with token credentials. Make sure to not have any extra characters or spaces in either the name or value.
  4. Create the azure.properties file via the Portal or kudu similar to what was done with the project.json file.subscription=xxx
    client=xxx
    key=xxx
    tenant=xxx
    managementURI=https://management.core.windows.net/
    baseURL=https://management.azure.com/
    authURL=https://login.windows.net/
    graphURL=https://graph.windows.net/

     

  5. Follow the steps here to get the required information for the Azure AD app registration: https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#sample-applications 
  6. Your file structure in the function folder should look something like this:
    azureauth.properties
    function.json
    project.json
    run.csx

     

  7. Create your Azure Activity Log alert and add in the URL of the Function. On the main blade of the Function in the top right hand corner there’s a link for </> Get function URL. You can also create the link by going to the “Integrate” section of the function and piecing together the required URL. It will look something like this:
    https://functionapp.azurewebsites.net/api/WebHookVMCreation2?code=zVBxxxx==&clientId=default
  8. Finally we add in our function code, linked below. The example I have is parsing through the JSON to find the required fields to pull all the required information. The log statements state what is happening at each step. Once we have the subscription, RG, and VM name we then make a call to the backend using the Azure SDK to pull the local IP address.

 

Project.json File: This is used to load the required auth dependencies from NPM

{

“frameworks”: {

“net46”:{

“dependencies”: {

“Microsoft.Azure.Management.Compute.Fluent” : “1.3.0”,

“Microsoft.Azure.Management.Fluent”: “1.3.0”,

“Microsoft.Azure.Management.ResourceManager.Fluent”: “1.3.0”,

}

}

}

}

azure.properties file used for authentication against the Azure backend

subscription=xxx

client=xxx

key=xxx

tenant=xxx

managementURI=https://management.core.windows.net/

baseURL=https://management.azure.com/

authURL=https://login.windows.net/

graphURL=https://graph.windows.net/

 

Function Code (run.csx):

#r “Newtonsoft.Json”

using System;

using System.Net;

using Newtonsoft.Json;

using Newtonsoft.Json.Linq;

using Microsoft.Azure.Management.Compute.Fluent;

using Microsoft.Azure.Management.Compute.Fluent.Models;

using Microsoft.Azure.Management.Fluent;

using Microsoft.Azure.Management.ResourceManager.Fluent;

using Microsoft.Azure.Management.ResourceManager.Fluent.Core;

public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)

{

    log.Info($”Webhook was triggered!”);

    log.Info($”Authenticating”);

    log.Info(Environment.GetEnvironmentVariable(“AZURE_AUTH_LOCATION”));

    var credentials = SdkContext.AzureCredentialsFactory.FromFile(Environment.GetEnvironmentVariable(“AZURE_AUTH_LOCATION”));

    var azure = Azure.Configure().WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic).Authenticate(credentials).WithDefaultSubscription();

    log.Info(credentials.ToString());

    log.Info(azure.ToString());

    string jsonContent = await req.Content.ReadAsStringAsync();

    dynamic data = JsonConvert.DeserializeObject(jsonContent);

    JObject parsed = JObject.Parse(jsonContent);

    log.Info($”Parsing Json”);

    foreach (var pair in parsed)

        {

            log.Info($”{pair.Key}: {pair.Value}”);

        }

    log.Info($”Parsing Complete…Creating Variables”);

    string vmId = data.data.context.activityLog.resourceId;

    log.Info($”Resource ID: {vmId}”);

    char[] delimiterChars = {‘/’};

    string[] VM = vmId.Split(delimiterChars);

    var vmName = VM[8];

    log.Info($”VM Name: {vmName}”);

    string groupName = data.data.context.activityLog.resourceGroupName;

    log.Info($”RG Name: {groupName}”);

    log.Info($”Getting VM info using VM and RG…”);

    var vm = azure.VirtualMachines.GetByResourceGroup(groupName, vmName);

    try{

   log.Info($”VM Info: {vm.ToString()}”);

    }

      catch (Exception e)

   {

       log.Info(e.ToString());

   }

   log.Info($”Getting the IP Address…”);

   try{

   var privateIP = vm.GetPrimaryNetworkInterface().IPConfigurations.Values;

    foreach (var x in privateIP)

            {

                log.Info($”Private IP Address: {x.PrivateIPAddress}”);

            }

   }

   catch (Exception e)

   {

       log.Info(e.ToString());

   }

      return req.CreateResponse(HttpStatusCode.OK, new

    {

        greeting = $”Success”

    });

}