Getting Started with Microsoft Azure

Introduction

While at Microsoft Ignite in September I started playing around with some of the Azure cloud offerings such as Function Apps, Event Hub, and Cosmos DB. I was able to quickly connect the resources together to create a sample solution and wanted to document the process.

The sample solution will consist of a Xamarin application which allows a person to start a new work period by calling a Function App. The Xamarin application will also send location data to an Event Hub. We will create a Function App which subscribes to the Event Hub as a processor and stores the location in Cosmos DB. We will also update our Function App to send an email whenever it starts a new work period. Here is a diagram that shows the high-level architecture.

2017-11-07_085251

Everything we are going to build can be done with a free Azure account which you can sign-up for today. It includes $200 to spend on any resources you would like. In addition you get access to over 25 products for free including 5GB of Cosmos DB, 1,000,000 Function requests per month, and 8,000 messages per day in the IOT Hub. The Xamarin application we are going to build can be created with Visual Studio Community addition which is free and runs on both Windows and macOS.

Xamarin Application

Once you have installed Visual Studio go ahead and run it. From the file menu select File -> New -> Project. Select Cross-Platform and then select the Cross Platform App (Xamarin) project template. If you do not see Cross-Platform as an option please run the Visual Studio installer and make sure you have the Mobile development with .NET workload installed. Now give your project a name and click OK. Select the Blank App template. Make sure that Xamarin.Forms and Shared Project are selected as shown below and then click OK.

2017-11-06_093846

2017-11-06_094012

2017-11-06_094408

When prompted for which version of Windows SDK you can leave it the default or change it to the specific version you wish to target. Here is the documentation to help choose a SDK version.

Since I am an Android phone user I am going to be running the application on a real Android device. You can choose to run the application in one of the bundled emulators for Android or iOS or just run the UWP application directly on your development machine. One recommendation I have if you are doing Android development is to use Vysor to mirror your device’s screen on your development machine. It saves time not having to pick-up the device and interact with it physically. It also allows me to leave an Android device plugged into my development machine and remote into it and continue developing without having to be physically sitting at the machine.

If you run the application now you see a Welcome to Xamarin.Forms screen displayed.

screenshot-1509984668568

Let’s add a button to the screen. There should be 4 projects in the solution [AppName].Android, [AppName].iOS, [AppName].UWP, and one just [AppName]. To keep from referring to the solution as [AppName] we will assume our app is named Demo01. Not a huge improvement, but at least it is a name. The project Demo01 is our Xamarin.Forms shared library which will contain all of our changes for this sample. Open MainPage.xaml and copy the following code just below the existing label control.

 <Button x:Name="StartWorkPeriodButton" Clicked="StartWorkPeriod_Clicked" Text="Start Work Period" VerticalOptions="StartAndExpand" HorizontalOptions="CenterAndExpand" />

We will use this button to submit an HTTP request to a Function App. Now open the MainPage.xaml.cs file. Copy the code below.

private const string StartWorkPeriodUri = "[FUNCTION_APP_URL_GOES_HERE]";
private static readonly HttpClient client = new HttpClient(); 

private void StartWorkPeriod_Clicked(object sender, EventArgs e)
{
   Console.WriteLine("Start WorkPeriod Clicked");
   StartWorkPeriod();
}

public static async Task StartWorkPeriod()
{
  var workPeriod = new
  {
    PersonId = "1",
    PersonName = "John Doe",
    WorkPeriodStartDateTime = DateTime.UtcNow.ToString("o")
  };

  var workPeriodSerialized = JsonConvert.SerializeObject(workPeriod);
  // Wrap our JSON inside a StringContent which then can be used by the HttpClient class
  var httpContent = new StringContent(workPeriodSerialized, Encoding.UTF8, "application/json");

  var response = await client.PostAsync(StartWorkPeriodUri, httpContent);

  var responseString = await response.Content.ReadAsStringAsync();
}

You will need to add the following using statements.

using Newtonsoft.Json;
using System.Net.Http;

You will need to add a System.Net.Http reference to the Demo01.Android project as well as add the Newtonsoft.Json Nuget package. Now we should be able to compile and run and see our new button. If we click the button our application throws an exception because we haven’t yet created our Function App.

screenshot-1509987757567

Function App

Let’s go ahead and create our first Function App. I am assuming you have already created your Azure account if not go do that right now. Once you have logged into the Azure Portal click New and search for Function App.

2017-11-06_120923

Select Function App from the list and click create. Give your Function App a name. You can leave the rest of the options alone if you want. Note that the default option is to create a new Resource Group named the same as your Function App. A Resource Group is a container that holds related resources for an Azure solution. The resource group can include all the resources for the solution, or only those resources that you want to manage as a group. You decide how you want to allocate resources to resource groups based on what makes the most sense for your organization. Here is a link to the Azure Resource Manager documentation.

2017-11-06_121431

Click create and give it a couple minutes to create the resources. Once it is complete you should be presented with a Function App page like below. Click the [+] icon next to Functions to create our first Function. We will be creating an API using the CSharp language so select those options if they are not the default and click Create this function.

2017-11-06_121629

2017-11-06_130012

The default function code is essentially a hello world which looks for a name parameter in the query parameters or request body and replies back hello. You can click the run button to make a test call to the API using the test settings on the right hand pane. There is a log of the Function activity at the bottom and a link to display the function URL in the top middle as shown below.

2017-11-06_131730

We are going to be performing two actions in our function. First we will store the WorkPeriod data passed to our API in a Cosmos DB document database. Then we will send an email using SendGrid. To get started we click Integrate then + New Output button. Select Azure Cosmos DB and click Select.

2017-11-06_132701

Document parameter name should be outputDocument. Database name should be outDatabase. Collection Name should be MyCollection. Select the checkbox to create a new Cosmos DB database. Enter /PartitionKey in the Partition Key field. Click new to create a new Azure Cosmos DB account. You will click Create New then provide a unique account ID. From the API dropdown select SQL which is the old Document DB API. Use your existing resource group that we created earlier and click Ok.

2017-11-06_132924

It will take a little while to create the account and database, but once it is done your Azure Cosmos DB account connection will be filled in and you can click save.

2017-11-06_134045

Now let’s add the code to write to Cosmos DB. Replace the Function code with the code below.

#r "Newtonsoft.Json"

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public static void Run(string req, out object outputDocument, TraceWriter log)
{
    log.Info($"C# Queue trigger function processed: {req}");

    dynamic data = JObject.Parse(req);

    string personId = data?.PersonId;
    string personName = data?.PersonName;
    string workPeriodStartDateTime = data?.WorkPeriodStartDateTime;

    string workPeriodId = Guid.NewGuid().ToString();

    outputDocument = new {
        PartitionKey = "workperiod",
        id = workPeriodId,
        personName = personName,
        personId = personId,
        workPeriodStartDateTime = workPeriodStartDateTime
    };
}

The #r pulls in the Nuget package for Newtonsoft.Json. The parameters are req which is a json string representing our WorkPeriod, outputDocument which is our object that gets stored in Cosmos DB, and log which allows our function to write to the log stream. To test our function copy the JSON below into the test window Request Body and click Run.

{
    "PersonId": "1",
    "PersonName": "John Doe",
    "WorkPeriodStartDateTime": "2017-11-05T19:27:42.627Z"
}

This will call our Function and should write a new document to Cosmos DB. To check if our document made it into Cosmos DB select Resource Groups from the left navigation. Then select your resource group you created the Cosmos DB in. Click the Cosmos DB resource.

2017-11-06_140057

Now select Data Explorer -> MyCollection -> Documents which should list one document. When you click on that document you should see the data in that document.

2017-11-06_140348

Now that we have created our Function App and tested it let’s configure our Xamarin application to call it. Naviagate back to our Function App by clicking Resource Groups -> Your Resource Group -> Function App. Then click the Get Function URL link and copy the URL.

2017-11-06_151339

Now paste that URL into your MainPage.xaml.cs file replacing the text “[FUNCTION_APP_URL_GOES_HERE]”.

private const string StartWorkPeriodUri = "[FUNCTION_APP_URL_GOES_HERE]";

Now run your application and click the Start Work Period button. If you refresh the Cosmos DB data explorer you should see another document now. It will be best to open the Cosmos DB Data Explorer in another tab as we will be referring back here several times.

Now lets add another button to our Xamarin application that will send location data to Azure Event Hub. We could send our location data directly to a new Function App API, but Event Hub is excellent at handling a large volume of events and provides a built-in pub/sub model. Copy the following code to MainPage.xaml.

<Button x:Name="SendLocationButton" Clicked="SendLocationButton_Clicked" Text="Send Location" VerticalOptions="StartAndExpand" HorizontalOptions="CenterAndExpand" />

Copy the following code to MainPage.xaml.cs.

private static EventHubClient eventHubClient;
private const string EhConnectionString = "[EVENTHUB_URL_GOES_HERE]";
private const string EhEntityPath = "location";

private void SendLocationButton_Clicked(object sender, EventArgs e)
{
  Console.WriteLine("Send Location Clicked");
  SendLocationEvent();
}

public static async Task SendLocationEvent()
{
  // Creates an EventHubsConnectionStringBuilder object from the connection string, and sets the EntityPath.
  // Typically, the connection string should have the entity path in it, but for the sake of this simple scenario
  // we are using the connection string from the namespace.
  var connectionStringBuilder = new EventHubsConnectionStringBuilder(EhConnectionString)
  {
    EntityPath = EhEntityPath
  };

  eventHubClient = EventHubClient.CreateFromConnectionString(connectionStringBuilder.ToString());
  var location = await GetCurrentLocation();
  if (location == null)
    return;

try
{
  var message = $@"{{""Latitude"":""{location.Latitude}"",""Longitude"":""{location.Longitude}""}}";

  Console.WriteLine($"Sending message: {message}");
  await eventHubClient.SendAsync(new EventData(Encoding.UTF8.GetBytes(message)));
}
catch (Exception exception)
{
  Console.WriteLine($"{DateTime.Now} > Exception: {exception.Message}");
}

await eventHubClient.CloseAsync();
}

public static async Task<Position> GetCurrentLocation()
{
  Position position = null;
  try
  {
  var locator = CrossGeolocator.Current;
  locator.DesiredAccuracy = 100;

  position = await locator.GetLastKnownLocationAsync();

  if (position != null)
  {
  //got a cahched position, so let's use it.
  return position;
  }

  if (!locator.IsGeolocationAvailable || !locator.IsGeolocationEnabled)
  {
  //not available or enabled
  return null;
  }

  position = await locator.GetPositionAsync(TimeSpan.FromSeconds(20), null, true);

  }
  catch (Exception ex)
  {
  //Display error as we have timed out or can't get location.
  }

  if (position == null)
  return null;

  var output = string.Format("Time: {0} \nLat: {1} \nLong: {2} \nAltitude: {3} \nAltitude Accuracy: {4} \nAccuracy: {5} \nHeading: {6} \nSpeed: {7}",
  position.Timestamp, position.Latitude, position.Longitude,
  position.Altitude, position.AltitudeAccuracy, position.Accuracy, position.Heading, position.Speed);

  Debug.WriteLine(output);

  return position;
}

Now we will install two Nuget packages Microsoft.Azure.EventHubs and Xam.Plugin.Geolocator. The Geolocator package provides cross-platform access to the location APIs.

Azure Event Hub

To create our Event Hub in the Azure Portal click New and search for Event Hub and click Create. Give the Event Hub a name, select the Basic pricing tier, and use your existing resource group. Click create.

2017-11-07_074433

Now we need to create an Event Hub end point. Click Event Hubs and + Event Hubs to add a new end point. Give the Event Hub the name location and click create.

2017-11-07_074557

2017-11-07_074657

Now we will get the Event Hub connection string by clicking the Connection Strings link on the overview screen.

2017-11-07_075212

Select the RootManageSharedAccessKey and copy the Connection string-primary key.

2017-11-07_075323

Back in our Xamarin application replace the text [EVENTHUB_URL_GOES_HERE] in MainPage.xaml.cs with your connection string. Now run the application and click the Send Location button a few times. If we go to the metrics dashboard in Azure for our Event Hub we should see those messages being received.

2017-11-07_080848

We have our location data making it into the Event Hub, but nothing interesting is happening with those messages. Let’s create a new Function App that will subscribe to our Event Hub and write those locations to Cosmos DB. In the Azure portal navigate to your existing Function App. Click the + icon. Select EventHubTrigger - C#. Click new for Event Hub connection, which will pop up a modal. Select the Event Hub we just created and click Select. Enter location for the Event Hub Name and click Create.

2017-11-07_081354

2017-11-07_081256

All the function does currently is write the Event Hub message it receives to the log. If you run your Xamarin application and click the Send Location button now you should see your location get written to the log console of your Function App.

2017-11-07_082620

So we have our Xamarin application sending the location to Event Hub and our Function App receiving it. Now we just need to store that location in Cosmos DB. Copy the code below and replace the code for your Function.

#r "Newtonsoft.Json"

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public static void Run(string myEventHubMessage, out object outputDocument, TraceWriter log)
{
    log.Info($"C# Event Hub trigger function processed a message: {myEventHubMessage}");
    dynamic data = JObject.Parse(myEventHubMessage);

    string personId = data?.PersonId;
    string latitude = data?.Latitude;
    string longitude = data?.Longitude;


    outputDocument = new {
        PartitionKey = "location",
        PersonId = personId,
        Latitude = latitude,
        Longitude = longitude,
    };
}

Now lets connect to our Cosmos DB.

  • Click the Integrate link

  • Click + New Output

  • Select Azure Cosmos DB

  • Click Select

  • Enter /PartitionKey in the Partition Key field

  • Click Save

2017-11-07_083108

Let’s test our code now. Copy the following into the test window request body.

{
"PersonId": "1",
"Latitude": "38.7634182",
"Longitude": "-84.381"
}

Click Run and if everything goes well you should be able to open the Data Explorer for our Cosmos DB and see our new location event.

img

Now click the Send Location button in the Xamarin application to send a few more location events to Cosmos DB. Refresh your Data Explorer query to see the new documents.

The last feature we will add is to have our Function App API send an email notifying that a new Work Period is being started. We will use SendGrid to handle the sending of emails. They offer a free account that can send up to 40,000 messages. Once you have created your account and logged into to the portal the first thing we need to do is create an API Key for our Function App. From the portal click Settings -> API Keys -> Create API Key

2017-11-07_210020

  • Enter a name for the key

  • Grant the permission to Send Mail

  • Click Create &amp; View
  • Copy the API Key

2017-11-07_210347

2017-11-07_210426

Now in another browser tab go to your Function App in the Azure Portal. Click the Application Settings link from the Overview page.

2017-11-07_211102

Add a new setting with the name SENDGRID_KEYand paste in your SendGrid API key for the value. Click save.

2017-11-07_211352

Click the HttpTriggerSharp1 function and let’s add a new file to the function called project.json.

2017-11-07_211722

Copy the following code into the file. This will pull the in the Sendgrid library from Nuget.

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Sendgrid": "9.9.0"
      }
    }
   }
}

Now select the run.csx file and replace the code with what’s below. Replace the To address placeholder [REPLACE_WITH_YOUR_EMAIL] with your email address if you wish to receive the email.

#r "Newtonsoft.Json"
#r "SendGrid"
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SendGrid;
using SendGrid.Helpers.Mail;
using System;

public static void Run(string req, out object outputDocument, TraceWriter log)
{
    log.Info($"C# Queue trigger function processed: {req}");

    dynamic data = JObject.Parse(req);

    string personId = data?.PersonId;
    string personName = data?.PersonName;
    string workPeriodStartDateTime = data?.WorkPeriodStartDateTime;

    string workPeriodId = Guid.NewGuid().ToString();

    outputDocument = new {
        PartitionKey = "workperiod",
        id = workPeriodId,
        personName = personName,
        personId = personId,
        workPeriodStartDateTime = workPeriodStartDateTime
    };

    var apiKey = Environment.GetEnvironmentVariable("SENDGRID_KEY");
    var client = new SendGridClient(apiKey);
    var from = new EmailAddress("test@example.com", "Field Service App");
    var subject = $"{personName} has Started a New Work Period";
    var to = new EmailAddress("[REPLACE_WITH_YOUR_EMAIL]", "Jane Doe");
    var plainTextContent = $"{req}";
    var htmlContent = $"{req}";
    var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
    var response = client.SendEmailAsync(msg);
}

We are ready to click Save and Run which will call our function and send the email. We can verify that SendGrid received our request by looking at the dashboard.

2017-11-07_212648

Summary

We have built a Xamarin mobile application that calls an Azure Function App API. The Function App stores a document in Cosmos DB and sends an email using the SendGrid service. The Xamarin app also uses the devices location services to get the devices current location and sends a message to Azure Event Hubs. We built a Function App that subscribes to the Event Hub and takes that location and stores it in Cosmos DB.

2017-11-07_085251

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s