Welcome to Global Azure Bootcamp 2019! All around the world user groups and communities want to learn about Azure! On April 27, 2019, all communities will come together once again in the sixth great Global Azure Bootcamp event! Each user group will organize their own one day deep dive class on Azure the way they see fit and how it works for their members. The result is that thousands of people get to learn about Azure and join together online under the social hashtag #GlobalAzure!
The 2019 Louisville Global Azure Bootcamp is a free one-day global training event on Azure, from the community to the community. See our event home page for more details.
This years format will be a blend of brief presentations, followed by hands-on and guided labs.
Our speakers include:
To get started you'll need the following pre-requisites. Please take a few moments to ensure everything is installed and configured.
Azure is big. Really big. Too big to talk about all things Azure in a single day.
We've assembled an exciting workshop to introduce you to several Azure services that cloud developers should know about:
In today's workshop, you'll get started with Azure and learn how to navigate through the Azure portal! You'll activate a free Azure subscription, learn about Resource Groups, and navigate through the Azure portal. You'll also create your development environment virtual machine running Visual Studio Community Edition.
Next, you'll learn about Platform as a Service (PaaS) offerings while creating a simple web app to manage data stored in an Azure SQL database. You'll finish by securing the data connection between your application and the database using an MSI (Managed Service Identity), then deploying to Azure services that host your web app.
Once your app is up and running, we'll teach you how your app can be restructured for scalability by separating the backend data access later into a separately-hosted ASP.NET Web API app. You'll also learn how to secure your service.
After you've built and deployed a REST API, you'll learn how you can use Azure Functions and Cosmos DB to aggregate your data and ready it for consumption by the public.
Finally, after developing a database of publicly-consumable data, we'll explore how to advertise the data publicly as an API using App Services, Web API, and API Management. See how it's easy to have customers request access to the data, subscribe to your service, and generate additional revenue.
Essentially...
You can find additional lab materials and presentation content at the locations below:
There are several ways to get an Azure subscription, such as the free trial subscription, the pay-as-you-go subscription, which has no minimums or commitments and you can cancel any time; Enterprise agreement subscriptions, or you can buy one from a Microsoft retailer. In this exercise, you'll create a trial subscription using the code you were given at the bootcamp.
Browse to https://www.microsoftazurepass.com/.
Use the Azure Code on the handout you were given to get started.
This concludes the exercise.
If you already have an Azure account
If you have an Azure account already, you can skip this section. If you have a Visual Studio subscription (formerly known as an MSDN account), you get free Azure dollars every month. Check out the next section for activating these benefits.
There are several ways to get an Azure subscription, such as the free trial subscription, the pay-as-you-go subscription, which has no minimums or commitments and you can cancel any time; Enterprise agreement subscriptions, or you can buy one from a Microsoft retailer. In exercise, you'll create a free trial subscription.
Browse to the following page http://azure.microsoft.com/en-us/pricing/free-trial/ to obtain a free trial account.
Click Start free.
Enter the credentials for the Microsoft account that you want to use. You will be redirected to the Sign up page.
Note
Some of the following sections could be omitted in the Sign up process, if you recently verified your Microsoft account.
If you already ahve an Azure subscription tied to your Microsoft account, you may see a screen like this:
You're wekcome you use your existing subscription for the bootcamp. If you're planningto use your existing subscription, you can skip this exercise. Click Sign Up to create a new subscription.
Complete step 1 by entering your mobile phone number.
Select Text me or Call me to verify that you are a real person. Typein the verification code you receive on the phone or via text.
Next, complete the Payment information section.
A Note about your Credit Card
Your credit card will not be billed, unless you remove the spending limits. If you run out of credit, your services will be shut down unless you choose to be billed.
Press Next after completing the credit card section.
In the Technical Support section, select the last option (No technical support) and click Next.
In the Agreement section, check the I agree to the subscription Agreement, offer details, and privacy statement option, and click Sign up.
Your free subscription will be set up, and after a while, you can start using it. Notice that you will be informed when the subscription expires.
Your free trial will expire in 29 days from it's creation.
If you happen to be a Visual Studio subscriber (formerly known as MSDN) you can activate your Azure Visual Studio subscription benefits. It is no charge, you can use your MSDN software in the cloud, and most importantly you get up to $150 in Azure credits every month. You can also get 33% discount in Virtual Machines and much more.
To active the Visual Studio subscription benefits, browse to the following URL: http://azure.microsoft.com/en-us/pricing/member-offers/msdn-benefits-details/
Scroll down to see the full list of benefits you will get for being a MSDN member. There is even a FAQ section you can read.
Click Activate your monthly Azure credit to activate the benefits.
You will need to enter your Microsoft account credentials to verify the subscription and complete the activation steps.
You might be wondering how you can participate in a cloud development workshop and not need any software installed.
Thanks to the Azure Resource Manager and some nifty templates I put together, we're going to provision a virtual machine (VM) with Visual Studio (and all the tools you'll need) installed in your Azure subscription. From that point forward, you can work from the VM.
It takes about 10 minutes to get the VM deployed to your subscription, so let's get started!
Start by clicking the Deploy to Azure button below.
This opens the Azure portal in a new tab of your browser. If you're prompted to sign in, do so.
When the page loads, you'll see this custom deployment page:
Resource Groups
Formally, resource groups provide a way to monitor, control access, provision and manage billing for collections of assets that are required to run an application, or used by a client or company department. Informally, think of resource groups like a file system folder, but instead of holding files and other folders, resource groups hold azure objects like storage accounts, web apps, functions, etc.
Naming Resource Groups
I like to name my resource groups after their purpose, and append them with -rg, which signifies they are a resource group.
WARNING
Do not forget your username and password. Write it down for today.
Scroll down to the bottom of the page and click two boxes:
Press the Purchase button.
After a few moments, the deployment of your VM will begin, and you'll see a status notification in the upper right:
...and a deployment tile on your dashboard:
Now, wait for about 10 minutes and your virtual machine will be deployed and ready to use.
That's it for the pre-requisites for today's workshop. Wait until your VM is created, and we'll be getting started soon!
Before we go any further, be sure you have all the pre-requisites downloaded and installed. You'll need the following:
NOTE
If you've been following along, you should have all of these above items.
One of the most important aspects of your Azure subscription and using the Azure portal is organization. You can create a lot of Azure resources very quickly in the portal, and it can become cluttered quickly. So, it's important to start your Azure subscription off right.
Our first stop will be to create a new Dashboard to organize our Azure resources we're building today.
We'll start by creating a dashboard.
Login to the Azure portal, click +, give the dashboard name, and click Done customizing.
That was easy! Dashboards are a quick way of organizing your Azure services. We like to create one for the workshop because it helps keep everything organized. You'll have a single place to go to find everything you build today.
Now that you have a new dashboard, let's put something on it. We'll be searching for the resource group you created in chapter 0 (the one that is holding your VM), and pinning it to this dashboard.
Resource Groups
You'll recall from the last chapter that resource groups provide a way to monitor, control access, provision and manage billing for collections of assets that are required to run an application, or used by a client or company department. Informally, think of resource groups like a file system folder, but instead of holding files and other folders, resource groups hold azure objects like storage accounts, web apps, functions, etc.
Start by searching for the resource group you created in chapter 0. My resource group was called workshop-rg.
Click in the search bar at the top. If you're lucky your resource group will be at the very top (like mine was). If not, type it's name and click on it.
This opens the resource group. Next, click the pin icon at the upper-right to pin the resource group to your dashboard:
Finally, close the resource group, by clicking the X in the upper right corner (next to the pin icon). You should see the resource group pinned to your dashboard:
Now that you have the VM's resource group pinned to your dashboard, it will be easy to locate the VM in later exercises.
Go ahead and click ont he Virtual machine, then pin it to the dashboard. When you're finished, you should see something like this:
That wraps up the basics of creating dashboard, creating resource groups, and pinning resources to a dashboard. We're not going to take a deep dive into Azure Resource Group. If you're interested in learning more, check out this article.
Next, let's get logged into the VM that we created in chapter 0.
Start by navigating to your Azure portal dashboard.
Locate the VM resource group you pinned earlier in this chapter and click on your virtual machine:
Click the Connect button.
This displays a download window on the right. Press the Download RDP file button.
This downloads a file to your computer that will open in your Remote Desktop program.
Click the downloaded file to open a connection to your VM. Enter your username and password you created earlier.
Click OK to connect.
If you're prompted by a security message, respond Yes:
You're now connected to your VM.
Download additional software
If you're like me, you have a standard toolset you like to use. Please, download software for your VM and don't forget your browser of choice, Notepad++, Visual Studio Code, etc.
This concludes the exercise.
Now that you're connected to your VM, you can continue to workshop from inside the VM.
Running a VM in Azure
If you're worried about excessive charges to your Azure subscription because you're running a VM constantly, don't worry. This VM is programmed to shut itself down every evening at 7:00 PM EST.
Let's get started by getting the master
branch.
Clone or download the master
branch from https://github.com/mikebranstein/global-azure-bootcamp-2019.
Use this link to download a zip file of the master
branch.
Unblock the .zip file!
Don't open the zip file yet. You may need to unblock it first!
If you're running Windows, right-click the zip file and go to the properties option. Check the Unblock option, press Apply, press Ok.
Now it's safe to unzip the file.
The Contoso University web app is a small app that is used to manage the faculty, students, courses, and grades of Contoso University. It's not very advanced, but comes with pre-seeded data, and is ideal to demonstrate the concepts of this workshop.
Before we can compile the solution, we need ot ensure .NET Core 2.2 is installed on your virtual machine.
Locate and run Visual Studio Installer from the Start Menu:
Select the Modify option under Visual Studio 2019:
On the right, expand the section labeled .NET Core cross-platform development, and check the box labeled .NET Core 2.2 development tools.
Click the Modify button on the bottom to install. The install takes approx. 5 minutes to complete.
When the install is finished, you can update Visual Studio 2019 to the latest version (if you want), but it isn't necessary.
Next, open the solution in Visual Studio 2019 by double-clicking the ContosoUniversity.sln
file in the src folder of the extracted files:
Logging into Visual Studio the first time
When you open Visual Studio the first time, it may take a few minutes. Be patient. You'll probably be prompted to sign in. Use your Microsoft account to sign in (the same one you used to sign up for the Azure trial).
The opened solution should look like this:
Build and debug the solution. You should see the Speech Recognition site load in your browser.
This concludes the exercise.
That's it! You're up and running and ready to move on! In the next section, you'll learn how to deploy your website to Azure.
In the last part of this chapter, you'll learn how to create an Azure Web App and deploy the Speech Service website to the cloud. In short, I like to think of Azure Web Apps like IIS in the cloud, but without the pomp and circumstance of setting up and configuring IIS.
Web Apps are also part of a larger Azure service called the App Service, which is focused on helping you to build highly-scalable cloud apps focused on the web (via Web Apps), mobile (via Mobile Apps), APIs (via API Apps), and automated business processes (via Logic Apps).
We don't have time to fully explore all of the components of the Azure App Service, so if you're interested, you can read more online.
As we've mentioned, Web Apps are like IIS in the cloud, but calling it that seems a bit unfair because there's quite a bit more to Web Apps:
Websites and Web Apps: Web Apps let developers rapidly build, deploy, and manage powerful websites and web apps. Build standards-based web apps and APIs using .NET, Node.js, PHP, Python, and Java. Deliver both web and mobile apps for employees or customers using a single back end. Securely deliver APIs that enable additional apps and devices.
Familiar and fast: Use your existing skills to code in your favorite language and IDE to build APIs and apps faster than ever. Access a rich gallery of pre-built APIs that make connecting to cloud services like Office 365 and Salesforce.com easy. Use templates to automate common workflows and accelerate your development. Experience unparalleled developer productivity with continuous integration using Visual Studio Team Services, GitHub, and live-site debugging.
Enterprise grade: App Service is designed for building and hosting secure mission-critical applications. Build Azure Active Directory-integrated business apps that connect securely to on-premises resources, and then host them on a secure cloud platform that's compliant with ISO information security standard, SOC2 accounting standards, and PCI security standards. Automatically back up and restore your apps, all while enjoying enterprise-level SLAs.
Build on Linux or bring your own Linux container image: Azure App Service provides default containers for versions of Node.js and PHP that make it easy to quickly get up and running on the service. With our new container support, developers can create a customized container based on the defaults. For example, developers could create a container with specific builds of Node.js and PHP that differ from the default versions provided by the service. This enables developers to use new or experimental framework versions that are not available in the default containers.
Global scale: App Service provides availability and automatic scale on a global datacenter infrastructure. Easily scale applications up or down on demand, and get high availability within and across different geographical regions. Replicating data and hosting services in multiple locations is quick and easy, making expansion into new regions and geographies as simple as a mouse click.
Optimized for DevOps: Focus on rapidly improving your apps without ever worrying about infrastructure. Deploy app updates with built-in staging, roll-back, testing-in-production, and performance testing capabilities. Achieve high availability with geo-distributed deployments. Monitor all aspects of your apps in real-time and historically with detailed operational logs. Never worry about maintaining or patching your infrastructure again.
Now that you understand the basics of web apps, let's create one and deploy our app to the cloud!
Earlier in this chapter, you created a resource group to house resources for this workshop. You did this via the Azure Portal. You can also create Web Apps via the Azure portal in the same manner. But, I'm going to show you another way of creating a Web App: from Visual Studio.
Visual Studio 2019 Warning
This exercise assumes you're running Visual Studio 2019. If you're not, please do.
From Visual Studio, right-click the ContosoUniversity project and select Publish. In the web publish window, select Microsoft Azure App Service, Create New, and press Publish. This short clip walks you through the process:
On the next page, give your Web App a name, select your Azure subscription, and select the Resource Group you created earlier (mine was named workshop-rg), and create a new Free App Service Plan. Read below for more details.
Unique Web App Names
Because a web app's name is used as part of it's URL in Azure, you need to ensure it's name is unique. Luckily, Visual Studio will check to ensure your web app name is unique before it attempts to create it. In other words, don't try to use the web app name you see below, because I already used it.
Click New... to create a new Web App plan.
Web App Plans
Web App plans describe the performance needs of a web app. Plans range from free (where multiple web apps run on shared hardware) to not-so-free, where you have dedicated hardware, lots of processing power, RAM, and SSDs. To learn more about the various plans, check out this article.
Create a new free plan.
After the plan is created, click Create to create the Web App in Azure.
When the Azure Web App is created in Azure, Visual Studio will publish the app to the Web App. After the publish has finished, your browser window will launch, showing you your deployed website.
Web App URLs
The deployed web app has a URL of Web App Name.azurewebsites.net. Remember this URL, because you'll be using it in later chapters.
One final note is to check the Azure Portal to see the App Service plan and Web App deployed to your resource group:
This concludes the exercise.
In this chapter you'll learn about Azure SQL Database, how to provision one in the Azure Portal, and how to use Entity Framework Core (EF Core) to seed a local (development database) and an Azure SQL Database.
Azure SQL Database is a general-purpose relational database-as-a-service (DBaaS) based on the latest stable version of Microsoft SQL Server Database Engine. SQL Database is a high-performance, reliable, and secure cloud database that you can use to build data-driven applications and websites in the programming language of your choice, without needing to manage infrastructure.
Now that you know what Azure SQL Database can do, let's start using it! You'll start by creating a SQL Database instance in the Azure portal.
SQL Servers and Databases
When creating a SQL Database, you also need to create a SQL Server. The steps below walk you through this process.
Start by jumping back to the Azure portal, and create a new resource by clicking the Create a resource button.
Search for SQL Database:
Fill out the required parameters as you create an instance:
Press the Select button after completing the server information in the right panel, then press Review and create.
When the SQL Server and database instances are provisioned, they appear in your resource group:
The final step is to navigate to the SQL Server instance by clicking on it.
Secured by default
As with most Azure resources, SQL Server is secured by default, meaning that you cannot access the SQL Server or databases without opening up the attached firewall. In this next step, we'll do just that.
Locate the Firewalls and virtual networks area, click Show firewall settings.
Click the Add client IP button and notice an entry being made in the table below. Then, click Save. This opens the SQL Server firewall to allow you to communicate with it directly for the duration of the workshop.
Next, navigate to the SQL Databases link on the left, and select the ContosoUniversity3 database.
Click the Show database connection strings link:
Copy the connection string and paste it into Notepad, or another text editor:
After pasting the connections tring into a text editor, locate the {your_username}
and {your_password}
sections and update them with the SQL Server username and password you created previously.
Save this connection string -- you'll need it later!
This concludes the exercise.
You may have noticed that the Contoso University app crashes when you navigate to various pages. That's because it's expecting a database exists, but we haven't created it.
In this next step, we'll use Entity Framework's Migrations to create and seed a local SQL database, then do the same for the Azure SQL Database we just created.
Every installation of Visual Studio 2019 comes with a development version of SQL Server called MS SQL Local DB. In fact, the Contoso University app is pre-configured to use this SQL Server instance already.
Open the appsettings.json
file to see the connection string:
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity3;Trusted_Connection=True;MultipleActiveResultSets=true"
}
Notice the server name of (localdb)\mssqllocaldb. This is the local development version of SQL I mentioned above. We'll use this in the next section to "seed" the database with Entity Framework Migrations.
Entity Framework and Migrations
If you're not familiar with Entity Framework and Migrations, don't worry. Entity Framework (EF) is a technology called an object-relational mapper (O/RM). O/RMs (like EF) enable .NET developers to work with a database using .NET objects, and eliminating the need for most of the data-access code they usually need to write (like stored procedures, views, and queries).
EF Migrations are a way to use .NET objects and code to create a SQL database and tables, then seed the database with preliminary data.
The Contoso University app already has Entity Framework installed and configured. Let's use it to create the ContosoUniversity3 database locally by running the migrations.
In Visual Studio, go to the Tools menu, select NuGet Package Manager, then Package Manager Console.
This opens a new area at the bottom of Visual Studio:
In the Package Manager Console, change the Default project to ContosoUniversityData, the type Update-Database and press Enter. The EF Migrations will read the connection string from your configuration file, create the ContosoUniversity3 database, create the tables, and load seed data into the tables. You can see the process happening below:
When the EF Migrations have finished, run the web site, and navigate to the Courses page. You will see the page displays a variety of course data:
This concludes the exercise.
Now that you've used EF Migrations, let's seed the ContosoUniversity3 database in Azure.
Open the appsettings.json
file to adjust the connection string. You'llrecall that we saved the Azure SQL Database connection string in our text editor earlier. Copy the value and paste it into the configuration file.
Mine looks like:
Now that the connection string has been updated, return to the Package Manager Console and run the EF Migrations again.
ContosoUniversityData project
Don't forget to change the default project to ContosoUniversityData before running the migrations.
> Update-Database
This will use the updated connection string to provision and seed the ContosoUniversity3 database in Azure.
When the migrations have finished, run the website to verify it still works.
Now, as a final step, re-deploy your web app to Azure, so it has the updated connection string (pointing to your Azure SQL Database).
This concludes the exercise.
That's it. In the next chapter, you'll learn how to secure your connection string so it isn't visible int he configuration file.
In this chapter, you'll learn:
Since the early days of .NET, developers have been putting SQL database connection strings in configuration files. This is not only a bad practice, but a serious security risk for several reasons:
Because of the risks of plain-text credentials, developers started encrypting the vaules, but this brought challenges with how to encrypt and decrypt, and made a developer's job more difficult.
There's got to be a better way!
Introducing Key Vault! Azure Key Vault helps solve the following problems:
So, with Key Vault, you store your connection strings inside of a secret store instead of in your app configuration file. Then, you simply store the location or URL of the Key Vault in your config file, so when your app starts up, you access the Key Vault, extract your secrets, then continue on as normal.
But wait...don't I need a secret/password to access a Key Vault
Yes you do, but it doesn't make much sense to store that secret inside of your app configuration file. Afterall, that's exactly what we're trying to avoid!
That's right. You can actually access the Key Vault without a password - at least a password that you're aware of.
The second critical component to securing your app secrets is something called a Managed Service Identity (or MSI for short). You can think of an MSI like a service account, but a service account that doesn't have a password.
Within Azure, you can assign an MSI to a Web App, so when the app runs it "acts" or "takes on" the role of the MSI. So, once it's been assigned to a Web App, you can give the Web App MSI permissions to access your Key Vault.
To the developer, this seems like magic - when you app runs, it becomes an MSI, then has permissions to access the Key Vault without a password. But behind the scenes Azure is managing the entire process, ensuring your Web App is the only resource using the assigned MSI.
Let's get started by assigning an MSI to your web app!
Navigate to the web app you deployed in an earlier chapter. Mine was called contoso-web-meb.
Scroll down on the left to find a section named Identity. Click on Identity:
On the System assigned tab, change the status to On and press Save.
Click Yes when prompted.
...and that's it! Your MSI (Managed Service Identity) is assigned.
This concludes the exercise.
Now that you have an MSI assigned to your web app, it's time to create a Key Vault and give the MSI access to read secrets.
In the Azure portal, search for the Key Vault by searching for it, like you did the SQL Database in an earlier chapter.
Place the Key Vault in the resource group you created for the workshop:
Click the Access policies section, then the + Add new link to add the web app MSI to the Key Vault's authorized users:
Do the following:
You should now see your web app added:
Click Ok to return to the Key Vault creation screen, and Create to provision the Key Vault.
It will take a few minutes to deploy the Key Vault. When it's finished provisioning, pin it to your dashboard (we'll be using it a lot today).
This concludes the exercise.
Now that you have created a Key Vault and assigned access to it, let's add a secret!
Navigate to your Key Vault and select the Secrets area on the left:
Click Generate/Import.
For the secret name, enter ConnectionStrings--DefaultConnection. Copy and paste your conneciton string from the app configuration file for the value.
Secret Names Matter
Go back and look at the app configuration file for Contoso University. You'll notice the JSON structure has a ConnectionStrings key following by a sub key of DefaultConnection. This is important.
In Key Vault, we'll mimick this struture by using the same key names and a double dash for the key name. Later, when we tell our web app to read the Key Vault secrets, it will be able to parse the secret name and dynamically replace the correct key/value pair in our configuration file.
Click Create.
This concludes the exercise.
Now that we have secrets in our Key Vault, let's configure the web app to access the Key Vault.
To configure the Contoso University app to read configuration data from the Key Vault, we need to modify the app's startup process to load it's current ocnfiguraiton file, then reach out to the Key Vault, read the secrets, and dynamically load the secret values into our configuration.
Let's start by adding a few NuGet packages to the ContosoUniversity project.
Right-click the Solution, and add the following packages. Be midful to add the specific version(s) we've specified below!
Next, create a new class in the root of the ContosoUniversity project named PrefixKeyVaultSecretManager.cs
. Place the following code inside.
using Microsoft.Azure.KeyVault.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureKeyVault;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ContosoUniversity
{
public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager
{
public string GetKey(SecretBundle secret)
{
// Remove the prefix from the secret name and replace two
// dashes in any name with the KeyDelimiter, which is the
// delimiter used in configuration (usually a colon). Azure
// Key Vault doesn't allow a colon in secret names.
return secret.SecretIdentifier.Name
.Replace("--", ConfigurationPath.KeyDelimiter);
}
public bool Load(SecretItem secret)
{
return true;
}
}
}
You can think of htis class as a conversion class. The GetKey() function is called each time a key/value pair is restrived from the Key Vault. The function then translates any double dashes into colons. This is an important step because .NET Core apps (like this one) natively translate colons into a heirarchical configuraiton file manner when parsing config values.
So, a value of ConnectionStrings--DefaultConnection is translated by the GetKey() function into ConnectionStrings:DefaultConnection. Then, .NET Core reads the string and replaces the configuration key of ConnectionStrings with sub key DefaultConnection with the value pulled from the Key Vault.
Why not use colons in the Key Vault secret?
You may be wondering why we had to convert double dashes into colons. Unfortunately, key vault key names cannot contain colons, so some type of replacement delimiter must be used. I chose a double dash.
Next, update the CreateWebHostBuilder() function in the Program.cs
file to tie everything together. After updaitng the function, don't forget to add the missing references at top.
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, builder) =>
{
// only run in "production" mode, so you don't override
// config values when running locally for development
if (context.HostingEnvironment.IsProduction())
{
var config = builder.Build();
// get MSI token from running web app
var tokenProvider = new AzureServiceTokenProvider();
// create kv client, passing MSI token for authorization
var keyvaultClient = new KeyVaultClient((authority, resource, scope)
=> tokenProvider.KeyVaultTokenCallback(authority, resource, scope));
// add the Key Vault "provider" that scans the KV, loading secrets into configuration
builder.AddAzureKeyVault(config["KeyVault:BaseUrl"], keyvaultClient, new PrefixKeyVaultSecretManager());
}
})
.UseStartup<Startup>();
These additional lines of code essentially does the following:
Finally, let's head on over to the configuration file and make a few changes:
Change the database connection string back to the MS SQL Local DB value (this way, we'll dev in our local environment):
Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity3;Trusted_Connection=True;MultipleActiveResultSets=true
Add a key/value pair for the KeyVault:BaseUrl. Be sure to substitute the URL of your Key Vault (you can find it in the Azure Portal).
"KeyVault": {
"BaseUrl": "{your_keyvault_url_goes_here}"
}
This is what my configuration file looks like when I'm finished:
To test your changes, run your app locally and add a new student. Then, publish your app to Azure. Navigate to the app's Azure URL and verify that the student you previously added isn't present.
This concludes the exercise.
Now that we've secured our app's secrets in the Key Vault, it's time to worry about the app itself! So far, anybody can access Contoso University's site and make changes. Let's lock it down.
This is a lot easier than you may think ;-)
Navigate to your web app in the Azure portal and select the "Authentication / Authorization" option on the left.
Change the App Service Authentication option to On, select Log in with Azure Active Directory form the drop down. Then, click Azure Active Directory authentication provider.
On the next screen, select Express management mode, and press Ok. Accept all other defaults.
Press Save at top.
That's it. Your app is now secured by Azure Active Directory. Anyone trying to access the site willbe automatically prompted to login. Try it. The first time you access it, it will prompt you to login (or detect that you're already logged into the Azure portal with the same credentials) and ask you for consent to read your login profile. Accept it.
This concludes the exercise.
Phew. This was a long chapter, but you learned some important cloud-native ways of securing secrets with the Key Vault.
In this chapter, you'll learn how to:
If you take a moment to investigate the ContosoUniversity MVC app, you'll notice that the MVC model and Entity Framework DbContext classes are split out in a shared library (ContosoUniversityData); however, much of the data access is being done in the controllers. This isn't "wrong", and it's a valid approach for small, simple applications.
Althgouh not "wrong" it can reduce code reuse and encourge developers to re-write the same type of code over and over again. For example, let's assume Contoso's accounting department needs access to student data in their application - all of a sudden it becomes easier for the developers working on that project to tie directly into the database, or write their own DbContext. And, before you know it, there are 4 different applications all attached to this database, extracting and manipulating data separately.
This is wrong, and it's a code smell.
Now, imaging that one of these teams needs a change made to how Students are tracked - how do you manage that across all the disparate applications? This is how an innocent seemingly-innocuous change can affect an entire organization downstream.
So, what's a devleoper to do?
There are a number of approaches and architectural patterns that exist to combat this scenario. In today's workshop, we're going to take a very niave, first-step approach to decoupling the web UI layer from the data access layer by creating a Web API project that abstracts the data access into a single, re-usable place. When that's finished, you'll have a single (and versioned) interface to access Contoso University data.
Let's get started.
Our first step is to create a Web API project.
In Visual Studio, add a new .NET Core 2.2 Web API Project to the existing solution.
Choose the ASP.NET Core Web Application project type from the Add New Project window:
Name the project ContosoUniversityAPI:
For the project type, ensure you've selected ASP.NET Core 2.2 and API. Click Create:
Next, you'll have to back-track a bit and complete the same steps from the previous chapter related to the Key Vault components, but doing so for the Web API project.
I'm not going to detail these steps for you here, because you can look. But I will give you the high-levle beats of what you'll be doing. Feel free to use this list as a TODO checklist:
In addition to these steps, you'll also need to configure the Entity Framework School DbContext class for the API project.
Copy the contents of the Program.cs Main() function, and move it into the API project's Program.cs file.
Finally, replace the Configuration() function in Startup.cs with the following:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
}
These code changes register the School DbContext class and ensure JSON data returned by REST API is complete.
Run the API project to test it. The Values Controller should return static values in a browser window:
This concludes the exercise.
Next, we'll be moving select data access logic from the Student and Courses controllers into Student and Courses controllers in the API project. Let's get started.
You may have noticed...
...there's a lot of data access logic in the Web controllers. I'm not walking you through moving every controller action. Instead, we'll be moving 2 controller actions, and I'll leave the others up to you. Think of it as a challenge (and practice for doing this IRL).
Now that we know the API project runs, delete the Values controller - it's not needed.
Let's start with the Courses controller. Create a Courses controller in the API project, then add the code below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ContosoUniversityData.Data;
using ContosoUniversityData.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversityAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CoursesController : ControllerBase
{
private readonly SchoolContext _context;
public CoursesController(SchoolContext context)
{
_context = context;
}
// GET: api/Courses
[HttpGet]
public IEnumerable<Course> Get()
{
var courses = _context.Courses
.Include(c => c.Department)
.AsNoTracking();
return courses.ToList();
}
}
}
Now, add a Students controller and the following code:
using System.Net;
using System.Threading.Tasks;
using ContosoUniversityData.Data;
using ContosoUniversityData.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversityApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentsController : ControllerBase
{
private readonly SchoolContext _context;
public StudentsController(SchoolContext context)
{
_context = context;
}
// POST: api/Students
[HttpPost]
public async Task<StatusCodeResult> Post([FromBody] Student student)
{
try
{
_context.Add(student);
await _context.SaveChangesAsync();
}
catch (DbUpdateException /* ex */)
{
return new StatusCodeResult((int)HttpStatusCode.BadRequest);
}
return new StatusCodeResult((int)HttpStatusCode.Created);
}
}
}
Finally, open the launchSettings.json file and modify the launchUrl value to be api/Courses in al places. Note - you will need to change it in 2 places.
As you can see, we added the HTTP GET for courses and HTTP POST for students. Run the API project to test your changes. You shoud see a list of Courses, returned as JSON:
This concludes the exercise.
Now that you've created the API project, it's time to refactor the Web project controllers to call the API project's REST endpoints instead of using Entity Framework.
Let's get to work.
Start with the Web Courses controller.
First, replace the CoursesController constructor with the following:
private readonly IConfiguration _configuration;
public CoursesController(SchoolContext context, IConfiguration configuration)
{
_context = context;
_configuration = configuration;
}
This will use ASP.NET Core's built-in dependency injection to obtain a reference to the app's configuration objects.
Next, replace the HTTP GET Index() method with the following:
// GET: Courses
public async Task<IActionResult> Index()
{
using (var httpClient = new HttpClient())
{
try
{
httpClient.BaseAddress = new Uri(_configuration["Api:BaseAddress"]);
var response = await httpClient.GetAsync($"{_configuration["Api:ApiPath"]}/api/Courses");
response.EnsureSuccessStatusCode();
var stringResult = await response.Content.ReadAsStringAsync();
var courses = JsonConvert.DeserializeObject<IEnumerable<Course>>(stringResult);
return View(courses);
}
catch (HttpRequestException httpRequestException)
{
return BadRequest($"Error getting request: {httpRequestException.Message}");
}
}
}
As you'll notice, this code creates an HTTP client, configures it to access a Uri (configured from the Api:BaseAddress configuration setting), then gets the resource at {Api:ApiPath}/api/Courses.
This means that we'll need to create a few configuration variables. Add values for Api:BaseAddress and Api:ApiPath to the configuration file:
"Api": {
"BaseAddress": "https://localhost:XXXXX",
"ApiPath": ""
}
BaseAdress needs customization
You'll notice that the code snippet above has http://localhost:XXXXX for the BaseAddress value. You'll need to replace the XXXXX with the port number your local API project is running on. Check the API project's launchSettings.json file for the sslPort value.
You'll also notice that the Api:ApiPath value is empty - that's OK. Sometimes, IIS hosts APIs in a virtual directory - and in fact, we'll be using it in a later chapter.
Next, update the Students controller constructor with this code:
private readonly IConfiguration _configuration;
public StudentsController(SchoolContext context, IConfiguration configuration)
{
_context = context;
_configuration = configuration;
}
Then, update the HTTP POST Create() method:
// POST: Students/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
[Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
{
if (ModelState.IsValid)
{
using (var httpClient = new HttpClient())
{
try
{
httpClient.BaseAddress = new Uri(_configuration["Api:BaseAddress"]);
var response = await httpClient.PostAsJsonAsync<Student>($"{_configuration["Api:ApiPath"]}/api/Students", student);
response.EnsureSuccessStatusCode();
return RedirectToAction(nameof(Index));
}
catch (HttpRequestException httpRequestException)
{
//Log the error (uncomment ex variable name and write a log.
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists " +
"see your system administrator.");
}
}
}
return View(student);
}
Just like the Course controller, the HttpClient class is used to make an HTTP request to the API project (using the same configuration variables).
To run both projects at once, right-click the solution, navigate to the Properties page, and under Startup Project, select Multiple startup projects and Start for the Web and API projects.
Run the projects, then:
This concludes the exercise.
Having a REST API deployed somewhere is just fine, but you need other developers know how they can interact with the API. That's where Swagger Specifications (a.k.a. OpenAPI Specifications) come in.
The OpenAPI Specification (OAS), formerly known as the Swagger Specification, is the world’s standard for defining RESTful interfaces. The OAS enables developers to design a technology-agnostic API interface that forms the basis of their API development and consumption.
You can learn more at Swagger's website if you're interested, but in summary, Swagger provides tools that can scan your API project and create a common-language specification document (in JSON) that describes all available REST commands for your API.
Swagger is easy to use, so let's get started.
To add Swagger to your API project, add the following NuGet packages (and specific versions) to your API project:
These packages add code generation capabilities to auto-generate a swagger.json file to describe your API and a middleware layer that hosts a Swagger UI to visualize and quickly test your API.
Next, update the ConfigurationServices() method of the Startup class:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ContosoUniversity", Version = "v1" });
});
}
Then update the Configure() method:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseHttpsRedirection();
app.UseMvc();
}
Launch the API project and navigate to https://localhost:XXXXX/swagger/v1/swagger.json. You should see a JSON file the browser:
Next, navigate to https://localhost:XXXXX/swagger to see the interactive UI:
Nice work! Take a few minutes to experiment with the Swagger UI - you'l notice that it has scanned your .NET code and determined there are 2 API endpoints: api/courses and api/students. You can even try out calls to it dynamically via the UI.
Exciting!
This concludes the exercise.
Now that we've tested our code locally, let's get it into Azure.
To deploy an test in Azure we have 3 steps:
Let's get started.
You've done this before with the Web project, so follow the same steps to create a new Web App. A few things to remember as you go through this process:
After publishing the API project, you'll notice that the site won't load:
That's because it cannot access your Key Vault! If you recall in the previous chapter, we added the Web project MSI to the access policies of the Key Vault. Now, we need to do the same steps for the API project.
I won't walk you through the details, but will outline the high-level steps and let you try it on your own:
Navigate to the Secrets area of your Key Vault and add the following key/value pairs:
Note: now that you've added these values, you may need to restart the API web app again to have it read the updated values.
You've done this before, so just republish. If you need help, check back at an earlier chapter.
After you've published the Web Site, navigate to it and try to:
This concludes the exercise.
In this chapter, you learned:
In this chapter, you'll learn how to:
At the end of the previous chapter, you had successfully deployed a REST API to an Azure Web App. The REST API had also integrated Swagger to let other know how to consume the API.
Deploying the REST API was a great accomplishment.
Deploying the REST API without a security layer could be problematic.
In this chapter, you'll work to address the API security layer issue with an Azure service called API Management.
What is API Management?
API Management (APIM) helps organizations publish APIs to external, partner, and internal developers to unlock the potential of their data and services. Businesses everywhere are looking to extend their operations as a digital platform, creating new channels, finding new customers and driving deeper engagement with existing ones. API Management provides the core competencies to ensure a successful API program through developer engagement, business insights, analytics, security, and protection. You can use Azure API Management to take any backend and launch a full-fledged API program based on it.
Sounds pretty cool, right? It is, and it's easy.
Creating an API Management service in Azure takes a little bit of time, so let's get started.
In the Azure portal, create a new API Management Resource. Choose a unique name for your resource. For other details, see below:
Click Create, then wait. It will take a while to deploy API Management - maybe up to 10 minutes, so this is a great opportunity to learn about securing your API project.
This concludes the exercise.
As I've discussed earlier, exposing a REST API service to the entire internet without any controls/restrictions in place isn't a great idea.
Let's learn how we can restrict access through the Azure portal.
Navigate to your deployed API project and find the Networking section of the Web App service. At the bottom of the page, click on the Configure Access Restrictions link:
On the Access Restrictions page, you have the ability to add firewall rules that restrict which IP addresses can access this service. This provides you with an easy way to ensure only certain individuals (or other Azure services) have access to your APIs.
As you can see, all internet traffic is allowed to access my API web app.
When your API Management Service is provisioned, it will have a Virtual IP address assigned to it - with that IP address, you'll be able to create a firewall rule that only allows traffic from API Management.
We'll revisit this page in the future. For now, take a break until API Management has finished deploying. Read through the next section as you're waiting.
This concludes the exercise.
Before we can start using the APIM service, there's a few concepts you should understand.
To use API Management, administrators create APIs. Each API consists of one or more operations, and each API can be added to one or more products. To use an API, developers subscribe to a product that contains that API, and then they can call the API's operation, subject to any usage policies that may be in effect. Common scenarios include:
The system is made up of the following components:
The API gateway is the endpoint that:
The Azure portal is the administrative interface where you set up your API program. Use it to:
The Developer portal serves as the main web presence for developers, where they can:
APIs are the foundation of an API Management service instance. Each API represents a set of operations available to developers. Each API contains a reference to the back-end service that implements the API, and its operations map to the operations implemented by the back-end service. Operations in API Management are highly configurable, with control over URL mapping, query and path parameters, request and response content, and operation response caching. Rate limit, quotas, and IP restriction policies can also be implemented at the API or individual operation level.
Products are how APIs are surfaced to developers. Products in API Management have one or more APIs, and are configured with a title, description, and terms of use. Products can be Open or Protected. Protected products must be subscribed to before they can be used, while open products can be used without a subscription. When a product is ready for use by developers, it can be published. Once it is published, it can be viewed (and in the case of protected products subscribed to) by developers. Subscription approval is configured at the product level and can either require administrator approval, or be auto-approved.
Groups are used to manage the visibility of products to developers. Products grant visibility to groups, and developers can view and subscribe to the products that are visible to the groups in which they belong.
Groups are used to manage the visibility of products to developers. API Management has the following immutable system groups:
Administrators - Azure subscription administrators are members of this group. Administrators manage API Management service instances, creating the APIs, operations, and products that are used by developers.
Developers - Authenticated developer portal users fall into this group. Developers are the customers that build applications using your APIs. Developers are granted access to the developer portal and build applications that call the operations of an API.
Guests - Unauthenticated developer portal users, such as prospective customers visiting the developer portal of an API Management instance fall into this group. They can be granted certain read-only access, such as the ability to view APIs but not call them.
In addition to these system groups, administrators can create custom groups or leverage external groups in associated Azure Active Directory tenants. Custom and external groups can be used alongside system groups in giving developers visibility and access to API products. For example, you could create one custom group for developers affiliated with a specific partner organization and allow them access to the APIs from a product containing relevant APIs only. A user can be a member of more than one group.
Developers represent the user accounts in an API Management service instance. Developers can be created or invited to join by administrators, or they can sign up from the Developer portal. Each developer is a member of one or more groups, and can subscribe to the products that grant visibility to those groups.
When developers subscribe to a product, they are granted the primary and secondary key for the product. This key is used when making calls into the product's APIs.
 Policies are a collection of statements that are executed sequentially on the request or response of an API. Popular statements include format conversion from XML to JSON and call rate limiting to restrict the number of incoming calls from a developer, and many other policies are available.
Policy expressions can be used as attribute values or text values in any of the API Management policies, unless the policy specifies otherwise. Some policies such as the Control flow and Set variable policies are based on policy expressions.
The developer portal is where developers can learn about your APIs, view and call operations, and subscribe to products. Prospective customers can visit the developer portal, view APIs and operations, and sign up. The URL for your developer portal is located on the dashboard in the Azure portal for your API Management service instance.
You can customize the look and feel of your developer portal by adding custom content, customizing styles, and adding your branding.
By now, your APIM service should have deployed. If not, take a break and continue once it's ready.
Earlier in this chapter you learned about APIM and how you can create an API that talks to a backend API. In this exercise, we'll be creating an APIM API that references your Contoso University REST API hosted in Azure.
Navigate to your APIM service and click the APIs option. Click the box OpenAPI under the Add a new API heading:
Swagger Specifications and OpenAPI
You may recall that Swagger Specifications were renamed to OpenAPI Specifications. You'll also recall that we added Swagger definitions to our API project. The OpenAPI option you just selected will let us reference the swagger.json files produced by Swagger to create an API in APIM automatically.
Click the Full link at top and complete the necessary fields, including:
It's important that you version your APIs. APIM provides many ways of versioning your API (via URL route, HTTP Headers, and query strings). I prefer the URL route, but what you choose is ultimately up to you.
At the very bottom, you can see a preview URL of what developers will use as the base URL when communicating with your API. For example, mine says https://contoso-university-meb.azure-api.net/contoso/v1. This base URL will be combined with my Azure-hosted API web app URL routing scheme (i.e., /api/Courses and /api/Students), so the final URL other developers will use will look like this: https://contoso-university-meb.azure-api.net/contoso/v1/api/Courses.
When an HTTP request is made to that URL, the APIM gateway willintercept the call, very the caller has permissions to access the API, then forward the request to the backend API (in this case https://contoso-web-api-meb.azurewebsites.net/api/Courses). When the backend API responds, it is routed back through the APIM gateway and back to the original caller.
Click Create to finish.
When APIM finishes creating the API gateway route, you're presented with detailed HTTP operations the APIM servie detected (via your Swagger file).
You can customize the data/routing/behavior of your API here, if you choose. This is out of scope for our workshop, but I encourage you to explore on your own.
Click the Settings tab of the newly-created API, and type the backend URL of your API into the Web service URL field. For me this was https://contoso-web-api-meb.azurewebsites.net:
Also select Unlimited from the Products area.
Click Save.
Unlimited Product
The unlimited product is a pre-created APIM product that gives developers unlimited access (without network throttling or other policies applied). It is also a product that requires Administrative appoval. So, when someone subscribes to the product, a request is sent to the API administrator, and it must be approved before the developer obtains an API subscription key.
Next, click the Test tab, then select the GET /api/Course command.
Here, you'll see a test client to testing your API. The Request URL is the URL that an HTTP GET will be sent to when you click the Send button at the bottom.
Click the Send button and you should see an HTTP 200 Success message:
It's normally a good idea to test access to your API from another tool. I happen to like Postman. Visit https://www.getpostman.com/ to download Postman.
After downloading and installing, create an HTTP GET request, and fill in the URL of your APIM API. For example, mine was https://contoso-university-meb.azure-api.net/contoso/v1/api/Courses.
Click Send.
As you can see, the response at the bottom says:
{
"statusCode": 401,
"message": "Access denied due to missing subscription key. Make sure to include subscription key when making requests to an API."
}
This is exactly what I'd expect, because I didn't supply a subscription key. When we placed the API behind the APIM gateway, it requires we submit a subscription key in the HTTP request header.
Let's try this again. We'll need to add an HTTP header named Ocp-Apim-Subscription-Key with a subscription key as the value.
To get a subscription key, navigate back to the APIM service, and click Developer portal. When the portal loads, you will be logged in as the portal Administrator.
Click your name in the upper-right and navigate to your profile. On the profile page, locate the Unlimited subscription (this is auto-assigned to you as an administrator), and click Show. Copy the subscription key and save it.
Return to Postman and create the key/value pair of:
Click Send.
This time, you'll notice the request was processed by the gateway, forwarded to the backend API, and returned a list of courses.
If you're up for a challenge, find a friend in the workshop that has also deployed APIM.
Get the URL of their developer portal, navigate to it, create a login, request an Unlimited product subscription, and make a successful request to their API.
If you're up for a second challenge, customize the look and feel of your portal.
This concludes the exercise.
You'll recall that we had reviewed the Access Restrictions section of your backend API web app earlier in this chapter. Let's follow-through with our plans to prevent anyone from casually querying the backend directly by resticting access to the APIM virtual IP address.
Navigate to the Overview section of your APIM service and locate the Virtual IP (VIP) Addresses:
Copy the VIP.
Next, navigate back tot he Access Restrictions area of your backend API web app. Add an Allow rule for the VIP you copied:
Click Add rule. You'l notice that the restrictions now show that all traffic is denied with the exception of the APIM VIP you just set:
To test the changes, navigate to your backend API URL directly from your browser. You should receive an error message.
Now, return to Postman and re-run your last HTTP GET request and verify that it succeeds.
This concludes the exercise.
In this chapter, you learned:
In this chapter, you'll learn:
In the last chapter, you deployed a secure API using API Management, then restricted access to your REST API so only API Management could communicate with it.
This is fantastic! But...now the Contoso University web app you deployed cannot talk to the backend API directly.
In this chapte,r we'll refactor the REST API access code slightly to communicate to the backend API via the APIM gateway.
To communicate through the APIM gateway, you'll need to update the code in the Contoso University web app. Let's get started!
There are a number of steps you'll need to accomplish to ensure the deployed Contoso University web app can speak through the APIM gateway:
Navigate to the Key Vault you deployed earlier, and add/update the following keys with their new values:
Open the web project's configuration file and add a "SubscriptionKey": "" key/value pair to the "Api" section. It should now look like:
"Api": {
"BaseAddress": "https://localhost:XXXXX",
"SubscriptionKey": "",
"ApiPath": ""
}
Open the Students contoller and update the Create() function to:
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
[Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
{
if (ModelState.IsValid)
{
using (var httpClient = new HttpClient())
{
try
{
httpClient.BaseAddress = new Uri(_configuration["Api:BaseAddress"]);
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", _configuration["Api:SubscriptionKey"]);
var response = await httpClient.PostAsJsonAsync<Student>($"{_configuration["Api:ApiPath"]}/api/Students", student);
response.EnsureSuccessStatusCode();
return RedirectToAction(nameof(Index));
}
catch (HttpRequestException httpRequestException)
{
//Log the error (uncomment ex variable name and write a log.
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists " +
"see your system administrator.");
}
}
}
return View(student);
}
Then, open the Courses controller and update it's Index() method to:
// GET: Courses
public async Task<IActionResult> Index()
{
using (var httpClient = new HttpClient())
{
try
{
httpClient.BaseAddress = new Uri(_configuration["Api:BaseAddress"]);
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", _configuration["Api:SubscriptionKey"]);
var response = await httpClient.GetAsync($"{_configuration["Api:ApiPath"]}/api/Courses");
response.EnsureSuccessStatusCode();
var stringResult = await response.Content.ReadAsStringAsync();
var courses = JsonConvert.DeserializeObject<IEnumerable<Course>>(stringResult);
return View(courses);
}
catch (HttpRequestException httpRequestException)
{
return BadRequest($"Error getting request: {httpRequestException.Message}");
}
}
}
Redeploy the ContosoUniversity web app to Azure and visit the site to make sure it still works.
This concludes the exercise.
Congratulations! You've successfully secured your API and accessed it from another application.
In this chapter, you'll learn:
You've completed our mission of creating secure, reusable REST APIs. But what if you want to share your data with an external audience. You've learned that API Management can help you share your APIs, but should an external audience hav access to the same data as your internal devleopment teams?
Definitely not.
In the last several chapters, we'll be endeavoring to build an external data mart accesible by another REST API hosted via APIM. This effort is part of a multi-university data-sharing program where you have agreed to share aggregate course, department, and student performance.
A key aspect to this type of data mart is that it's use does not affect the use of your internal APIs. For example:
To accomplish these goals, you'll be designing a system composed of 5 additional Azure resources:
We have a lot to build over the next several chapter, so let's get started.
We'll provision all of the Azure resources at once.
Create an Event Hub with the following settings (be sure to use your own unique name):
Creating the Event Hub actually creates an Event Hub Namespace. When it's finished deploying, navigate to it and create an Event Hub named dataupdated:
Next, navigate to the Shared access policies section, click on RootManageSharedAccessPolicy and copy the Primary key and Connection string primary key values:
Save these values for later.
Create a Function App with the following settings (be sure to use your own unique name for the function app and the storage account):
Create a Cosmos Db with the following settings (be sure to use your own unique name):
Create a Web App with the following settings to host the external Web API project (be sure to use your own unique name):
This concludes the exercise.
Now that our Azure resources are created, let's return to our code. The goal is to write a message to the Event Hub each time data is changed in our database, so that an Azure Function App (which is listening for messages on the Event Hub) can aggregate our internal data and write it to a Cosmos Db.
The best time to write a message on to the Event Hub is immediatly after data has been updated in the SQL database, which occurs in our central Web API REST service. But, it feels sloppy to add a bunch of code to every method that updates data.
Instead, it's better to create a decoupled process that can augment the Web API controller actions. Luckily, this si the exact purpose of Action Filters.
There are several types of ASP.NET Core action filters, but we're really only concerned with one that will run code after controller actions succeed.
So, if we do this the right way and put the Event Hub message logic in an action filter, we can annotate any controller action that updates SQL data with the filter. And viola! Decoupled design where we have a very low risk of affecting the code within our existing REST API methods.
Create a new .NET Core Class Library named ContosoUniversityInfra in your solution. It will store shared Event Hub message data types that can be access by the API project and a Function App project (to be provisioned in a latter chapter).
Add the following NuGet packages (being careful to use the exact versions specified):
Remove the Class1.cs
file.
Create a Models folder and add 2 classes: CourseAggregateDto and DemographicsDto. These classes will hold the aggregate course, department, and student performance demographic data that is stored in Cosmos Db.
Use the following code snippets to build out each class.
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace ContosoUniversityInfra.Models
{
public class CourseAggregateDto
{
[JsonProperty(PropertyName = "courseId")]
public int CourseId { get; set; }
[JsonProperty(PropertyName = "courseName")]
public string CourseName { get; set; }
[JsonProperty(PropertyName = "departmentId")]
public int DepartmentId { get; set; }
[JsonProperty(PropertyName = "departmentName")]
public string DepartmentName { get; set; }
[JsonProperty(PropertyName = "enrollmentCount")]
public int EnrollmentCount { get; set; }
[JsonProperty(PropertyName = "gradeACount")]
public int GradeACount { get; set; }
[JsonProperty(PropertyName = "gradeBCount")]
public int GradeBCount { get; set; }
[JsonProperty(PropertyName = "gradeCCount")]
public int GradeCCount { get; set; }
[JsonProperty(PropertyName = "gradeDCount")]
public int GradeDCount { get; set; }
[JsonProperty(PropertyName = "gradeFCount")]
public int GradeFCount { get; set; }
[JsonProperty(PropertyName = "gradeOtherCount")]
public int GradeOtherCount { get; set; }
}
}
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace ContosoUniversityInfra.Models
{
public class DemographicsDto
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "schoolYear")]
public int SchoolYear { get; set; }
[JsonProperty(PropertyName = "courses")]
public List<CourseAggregateDto> Courses { get; set; }
public DemographicsDto(int schoolYear)
{
this.SchoolYear = schoolYear;
this.Courses = new List<CourseAggregateDto>();
}
}
}
Add a class named DataUpdatedMessage to the root of the project. Use the following code to complete the class:
using System;
namespace ContosoUniversityInfra
{
public class DataUpdatedMessage
{
public MessageType MessageType { get; set; }
}
public enum MessageType
{
None = 0,
Rebuild = 1
}
}
Add a few staple NuGet packages to the ContosoUniversityApi project (be mindful of the specific versions we're referencing):
Add a reference to the ContosoUniversityInfra project.
In the ContosoUniversityApi project, create a folder named Filters and add a class named PublishDataUpdatedEventActionFilter to the folder. Populate the class with the following code:
using ContosoUniversityInfra;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Azure.EventHubs;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ContosoUniversityApi.Filters
{
public class PublishDataUpdatedEventActionFilter : IActionFilter
{
private readonly IConfiguration _configuration;
public PublishDataUpdatedEventActionFilter(IConfiguration configuration)
{
_configuration = configuration;
}
// runs after a controller action completed
public void OnActionExecuted(ActionExecutedContext context)
{
// construct a message to send on the event hub
var message = new DataUpdatedMessage() { MessageType = MessageType.Rebuild };
// send the message
SendMessageToEventHub(message, _configuration["EventHub:ConnectionString"])
.GetAwaiter()
.GetResult();
}
public void OnActionExecuting(ActionExecutingContext context)
{
return; // do nothing
}
private static async Task SendMessageToEventHub(DataUpdatedMessage message, string eventHubConnectionString)
{
// Creates an EventHubsConnectionStringBuilder object from the connection string, and sets the EntityPath.
// Typically, the connection string should have the entity path in it, but this simple scenario
// uses the connection string from the namespace.
var connectionStringBuilder = new EventHubsConnectionStringBuilder(eventHubConnectionString)
{
EntityPath = "dataupdated"
};
var eventHubClient = EventHubClient.CreateFromConnectionString(connectionStringBuilder.ToString());
await eventHubClient.SendAsync(new EventData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message))));
await eventHubClient.CloseAsync();
}
}
}
Next, open the API project configuration file and add a value for EventHub:ConnectionString that contains the Event Hub connection string you saved earlier in this chapter.
"EventHub": {
"ConnectionString": "{your event hub connection string here}"
}
After updating the configuration file, add the same value in the Azure Key Vault.
Event Hubs and Development
Azure Event Hubs do not have a solution for developing locally, so you have to use a real Event Hub for local development. Usually, I would have a separate Event Hub I use for development and production work, but to same time in the workshop, we're using the same Event Hub. So, when you get back to work, do what I say, not what I do ;-)
Before you can use the Action Filter you need to register it in the API project's startup process.
Update the ConfigureServices() function in the Startup class with the following code:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<PublishDataUpdatedEventActionFilter>();
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ContosoUniversity", Version = "v1" });
});
}
You'll notice we added a line to register the PublishDataUpdatedEventActionFilter class.
The final step is to apply the action filter to any controller actions that update data. Because our solution is purposefully small, the only qualifying controller action is the Student contoller's Create() method. Update it with the followin code:
// POST: api/Students
[HttpPost]
[ServiceFilter(typeof(PublishDataUpdatedEventActionFilter))]
public async Task<StatusCodeResult> Post([FromBody] Student student)
{
try
{
_context.Add(student);
await _context.SaveChangesAsync();
}
catch (DbUpdateException /* ex */)
{
return new StatusCodeResult((int)HttpStatusCode.BadRequest);
}
return new StatusCodeResult((int)HttpStatusCode.Created);
}
You'll notice the annotation applying the action filter.
Publish the API project to Azure, open the Contoso University web app, and create a new Student.
Wait approx 2-3 minutes, then navigate to the Event Hub Overview page in the Azure portal. Switch the metic type to Messages and look for a spike on the chart, indicating that a message was sent to the Event Hub when the student was created.
This concludes the exercise.
Now that the Event Hub has our messages, it's time to create an Azure Function to listen for the message and build the course, department, and student performance demographic data.
Add an Azure Function project named ContosoUniversityFunction to the Visual Studio solution:
When specifying the details for the Azure Function project, select Azure Functions v2 (.NET Core), choose the Event Hub trigger, Storage Emulator for the storage account, EventHubConnectionString for the connection string setting, and dataupdated as the Event Hub name:
Click Ok.
This will add the Azure Function app project to the solution. Unfortunately, it add the Function project as a .NET Core 2.1 project.
Immediately right-click the project, select Properties, and set the Target Framework to .NET Core 2.2:
Remove the Function1.cs file.
Next, add project references to the following projects:
Add the follow NuGet Packages:
Open the local.settings.json file and add a key under the Values area for EventHubConnectionString. Set the value to the Event Hub conenction string you saved earlier in this chapter. Your file should look like this:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"EventHubConnectionString": "{your event hub connection string here}"
}
}
Next, create a class named UpdateDataFunction and replace it's contents with the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.EventHubs;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using ContosoUniversityInfra;
using ContosoUniversityData.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Azure.Documents.Client;
using ContosoUniversityInfra.Models;
using Microsoft.Azure.Documents.Linq;
namespace ContosoUniversityFunction
{
public static class UpdateDataFunction
{
[FunctionName("UpdateData")]
public static async Task Run([EventHubTrigger("dataupdated", Connection = "EventHubConnectionString")] EventData[] events, ILogger log)
{
var exceptions = new List<Exception>();
foreach (EventData eventData in events)
{
try
{
string messageBody = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count);
var message = JsonConvert.DeserializeObject<DataUpdatedMessage>(messageBody);
if (message.MessageType == MessageType.Rebuild)
{
log.LogInformation($"C# Event Hub trigger function processed a message: {messageBody}");
}
await Task.Yield();
}
catch (Exception e)
{
// We need to keep processing the rest of the batch - capture this exception and continue.
// Also, consider capturing details of the message that failed processing so it can be processed again later.
exceptions.Add(e);
}
}
// Once processing of the batch is complete, if any messages in the batch failed processing throw an exception so that there is a record of the failure.
if (exceptions.Count > 1)
throw new AggregateException(exceptions);
if (exceptions.Count == 1)
throw exceptions.Single();
}
}
}
This code is a standard Azure Function definition that is bound to the Event Hub, and runs each time a message is sent to the hub.
Before we test our code, you'll have to start the Azure Storage Emulator locally. Storage Emulator is a locally-running application that creates a storage account for use by the Azure Function.
In the start menu of your workshop VM, type Storage Emulator and it will appear in the list of available apps. CLick it to run. Wait approx. 1 minute while it runs for the first time:
That's it! You should see a message saying it started successfully.
Now that you've written you Function, run it in Visual Studio. Change the startup project to the Function project and debug!
Near the end of the file, you'll see a message indicating that a message was received from the Event Hub and processed. This means everything is wired up correctly.
This concludes the exercise.
Before we continue, let's take a short break and learn a little about Cosmos Db.
Today’s applications are required to be highly responsive and always online. To achieve low latency and high availability, instances of these applications need to be deployed in datacenters that are close to their users. Applications need to respond in real time to large changes in usage at peak hours, store ever increasing volumes of data, and make this data available to users in milliseconds.
Azure Cosmos DB is Microsoft's globally distributed, multi-model database service. With a click of a button, Cosmos DB enables you to elastically and independently scale throughput and storage across any number of Azure regions worldwide. You can elastically scale throughput and storage, and take advantage of fast, single-digit-millisecond data access using your favorite API including SQL, MongoDB, Cassandra, Tables, or Gremlin. Cosmos DB provides comprehensive service level agreements (SLAs) for throughput, latency, availability, and consistency guarantees, something no other database service offers.
Turnkey global distribution
Cosmos DB enables you to build highly responsive and highly available applications worldwide. Cosmos DB transparently replicates your data wherever your users are, so your users can interact with a replica of the data that is closest to them.
Cosmos DB allows you to add or remove any of the Azure regions to your Cosmos account at any time, with a click of a button. Cosmos DB will seamlessly replicate your data to all the regions associated with your Cosmos account while your application continues to be highly available, thanks to the multi-homing capabilities of the service. For more information, see the global distribution article.
Always On
By virtue of deep integration with Azure infrastructure and transparent multi-master replication, Cosmos DB provides 99.999% high availability for both reads and writes. Cosmos DB also provides you with the ability to programmatically (or via Portal) invoke the regional failover of your Cosmos account. This capability helps ensure that your application is designed to failover in the case of regional disaster.
Elastic scalability of throughput and storage, worldwide
Designed with transparent horizontal partitioning and multi-master replication, Cosmos DB offers unprecedented elastic scalability for your writes and reads, all around the globe. You can elastically scale up from thousands to hundreds of millions of requests/sec around the globe, with a single API call and pay only for the throughput (and storage) you need. This capability helps you to deal with unexpected spikes in your workloads without having to over-provision for the peak. For more information, see partitioning in Cosmos DB, provisioned throughput on containers and databases, and scaling provisioned throughput globally.
Guaranteed low latency at 99th percentile, worldwide
Using Cosmos DB, you can build highly responsive, planet scale applications. With its novel multi-master replication protocol and latch-free and write-optimized database engine, Cosmos DB guarantees less than 10-ms latencies for both, reads and (indexed) writes at the 99th percentile, all around the world. This capability enables sustained ingestion of data and blazing-fast queries for highly responsive apps.
Precisely defined, multiple consistency choices
When building globally distributed applications in Cosmos DB, you no longer have to make extreme tradeoffs between consistency, availability, latency, and throughput. Cosmos DB’s multi-master replication protocol is carefully designed to offer five well-defined consistency choices - strong, bounded staleness, session, consistent prefix, and eventual — for an intuitive programming model with low latency and high availability for your globally distributed application.
No schema or index management
Keeping database schema and indexes in-sync with an application’s schema is especially painful for globally distributed apps. With Cosmos DB, you do not need to deal with schema or index management. The database engine is fully schema-agnostic. Since no schema and index management is required, you also don’t have to worry about application downtime while migrating schemas. Cosmos DB automatically indexes all data and serves queries fast.
Battle tested database service
Cosmos DB is a foundational service in Azure. For nearly a decade, Cosmos DB has been used by many of Microsoft’s products for mission critical applications at global scale, including Skype, Xbox, Office 365, Azure, and many others. Today, Cosmos DB is one of the fastest growing services on Azure, used by many external customers and mission-critical applications that require elastic scale, turnkey global distribution, multi-master replication for low latency and high availability of both reads and writes.
Ubiquitous regional presence
Cosmos DB is available in all Azure regions worldwide, including 54+ regions in public cloud, Azure China 21Vianet, Azure Germany, Azure Government, and Azure Government for Department of Defense (DoD). See Cosmos DB’s regional presence.
Secure by default and enterprise ready
Cosmos DB is certified for a wide array of compliance standards. Additionally, all data in Cosmos DB is encrypted at rest and in motion. Cosmos DB provides row level authorization and adheres to strict security standards.
Significant TCO savings
Since Cosmos DB is a fully managed service, you no longer need to manage and operate complex multi datacenter deployments and upgrades of your database software, pay for the support, licensing, or operations or have to provision your database for the peak workload. For more information, see Optimize cost with Cosmos DB.
Industry leading comprehensive SLAs
Cosmos DB is the first and only service to offer industry-leading comprehensive SLAs encompassing 99.999% high availability, read and write latency at the 99th percentile, guaranteed throughput, and consistency.
Globally distributed operational analytics with Spark
You can run Spark directly on data stored in Cosmos DB. This capability allows you to do low-latency, operational analytics at global scale without impacting transactional workloads operating directly against Cosmos DB. For more information, see Globally distributed operational analytics.
Develop applications on Cosmos DB using popular NoSQL APIs
Cosmos DB offers a choice of APIs to work with your data stored in your Cosmos database. By default, you can use SQL (a core API) for querying your Cosmos database. Cosmos DB also implements APIs for Cassandra, MongoDB, Gremlin and Azure Table Storage. You can point client drivers (and tools) for the commonly used NoSQL (e.g., MongoDB, Cassandra, Gremlin) directly to your Cosmos database. By supporting the wire protocols of commonly used NoSQL APIs, Cosmos DB allows you to:
Now that we know a little bit aobut Cosmos Db and also know the Azure Function can process Event Hub messages, let's build out the data aggregation pipeline. First, we'll create a Cosmos Db collection to hold our demographics data.
Navigate to the Cosmos Db you created earlier in the workshop. On the Overview tab, click + Add Collection:
This will prompt you to create a new Database Container. Enter the following values:
Next, navigate to the Keys area of the Cosmos Db and copy the follwoing values:
Save them for later.
This concludes the exercise.
In this next exercise, we'll be updating the Azure function to write to Cosmos Db.
Return to Visual Studio and replace the UpdateDataFunction class with the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.EventHubs;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using ContosoUniversityInfra;
using ContosoUniversityData.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Azure.Documents.Client;
using ContosoUniversityInfra.Models;
using Microsoft.Azure.Documents.Linq;
namespace ContosoUniversityFunction
{
public static class UpdateDataFunction
{
[FunctionName("UpdateData")]
public static async Task Run([EventHubTrigger("dataupdated", Connection = "EventHubConnectionString")] EventData[] events, ILogger log)
{
var exceptions = new List<Exception>();
var options = new DbContextOptionsBuilder<SchoolContext>();
options.UseSqlServer(Environment.GetEnvironmentVariable("ConnectionString"));
var context = new SchoolContext(options.Options);
foreach (EventData eventData in events)
{
try
{
string messageBody = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count);
var message = JsonConvert.DeserializeObject<DataUpdatedMessage>(messageBody);
if (message.MessageType == MessageType.Rebuild)
{
log.LogInformation($"C# Event Hub trigger function processed a message: {messageBody}");
// recalc CosmosDb for students
// get demographics from cosmos db
var client = new DocumentClient(new Uri(Environment.GetEnvironmentVariable("CosmosDbUri")), Environment.GetEnvironmentVariable("CosmosDbAuthKey"));
var queryOptions = new FeedOptions { MaxItemCount = -1 };
var query = client.CreateDocumentQuery<DemographicsDto>(
UriFactory.CreateDocumentCollectionUri("demographics", "demographics"), queryOptions)
.Where(d => d.SchoolYear == 2019)
.AsDocumentQuery();
var results = new List<DemographicsDto>();
while (query.HasMoreResults)
results.AddRange(await query.ExecuteNextAsync<DemographicsDto>());
DemographicsDto demographicsDto =
results.Count == 0 ?
new DemographicsDto(2019) :
results[0];
// clean out existing courses b/c we're going to rebuild it
demographicsDto.Courses.Clear();
var courses = await context.Courses
.Include(i => i.Department)
.Include(i => i.Enrollments)
.ToListAsync();
foreach (var course in courses)
{
demographicsDto.Courses.Add(new CourseAggregateDto()
{
CourseId = course.CourseID,
CourseName = course.Title,
DepartmentId = course.DepartmentID,
DepartmentName = course.Department.Name,
EnrollmentCount = course.Enrollments.Count,
GradeACount = course.Enrollments.Count(e => e.Grade == ContosoUniversityData.Models.Grade.A),
GradeBCount = course.Enrollments.Count(e => e.Grade == ContosoUniversityData.Models.Grade.B),
GradeCCount = course.Enrollments.Count(e => e.Grade == ContosoUniversityData.Models.Grade.C),
GradeDCount = course.Enrollments.Count(e => e.Grade == ContosoUniversityData.Models.Grade.D),
GradeFCount = course.Enrollments.Count(e => e.Grade == ContosoUniversityData.Models.Grade.F),
GradeOtherCount = course.Enrollments.Count(e => !e.Grade.HasValue)
});
}
// check if document exists, upsert or replace
if (demographicsDto.Id == null)
await client.UpsertDocumentAsync(UriFactory.CreateDocumentCollectionUri("demographics", "demographics"), demographicsDto);
else
await client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri("demographics", "demographics", demographicsDto.Id), demographicsDto);
}
await Task.Yield();
}
catch (Exception e)
{
// We need to keep processing the rest of the batch - capture this exception and continue.
// Also, consider capturing details of the message that failed processing so it can be processed again later.
exceptions.Add(e);
}
}
// Once processing of the batch is complete, if any messages in the batch failed processing throw an exception so that there is a record of the failure.
if (exceptions.Count > 1)
throw new AggregateException(exceptions);
if (exceptions.Count == 1)
throw exceptions.Single();
}
}
}
This updated code queries Cosmos Db for the demographic data. It then uses Entity Framework to query enrolment and student performance stats for each course. Finally, it either creates a new entry in Cosmos or updates the existing entry.
Due to time constraints, we won't spend time explaining the details any further; however, you're encouraged to continue exploring on your own.
Next, add several configuration settings to the local.settings.json file in the Values section:
"ConnectionString": "{your Azure SQL Server connection string}",
"CosmosDbUri": "{your Cosmos Db URI}",
"CosmosDbAuthKey": "{your Cosmos Db PRIMARY KEY}"
Run the Azure function, then navigate to the Azure-hosted Contoso University web app and add a Student.
You should see the Function app console window respond almost immediately, indicating it aggregated the data and wrote it to Cosmos Db.
Stop the Function app. Navigate to the Cosmos Db service in the Azure portal. Locate the Data Explorer area and find the list of documents stored int he demographics container. You shoudl see the document just created by the Azure function:
This concludes the exercise.
Right-click the Function app and select Publish. When prompted, Select an Existing Function app and locate the app you created earlier in this chapter.
This will compile your Function and deploy it to Azure.
After you've deployed to Azure, navigate to the Function app configuration area:
Add several app settings into the Configuration area:
NOTE: These are the same values you had locally stored in the local.settings.json file.
Save the settings. Return to the main Function page and restart the Function app.
At the beginning of this chapter, we outlined a multi-step process to creating an external REST API. Together, we've completed 75% of that work. We have created the first 3 of these items:
With what you've learned in this workshop, I feel confident you should be able to complete #4 and #5 on your own. The workshop isn't over, and I'll be around to help out when needed or if you get stuck.
Good luck!


Save a SNAPverse diagram to disk. The default verse
is "complete"
. It is the only option at this time. More coming soon.
Other arguments are passed to DiagrammeR::export_graph
.
verse_diagram(verse = "complete", file_name = "sv_td_all.svg", file_type = NULL, title = NULL, width = 350, height = NULL)
| verse | character, type of verse flowchart diagram. Defaults to |
|---|---|
| file_name | output file name, defaults to |
| file_type | the output file type. |
| title | character, flowchart title. |
| width | numeric. |
| height | numeric. |
side effect of saving image file to disk.
# NOT RUN { verse_diagram(title="Simplified overview of the SNAPverse") # }


Demo One - Basic Knockout
Info
Name:
Twitter:
Email:
Change Info
First Name:
Last Name:
Add New Pet
Pets
Type:
Age:


From Mr. Thomas MacDonald, now gamekeeper at Dunrobin.
WHERE Loch Ness now is, there was long ago a fine glen. A woman went one day to the well to fetch water, and she found the spring flowing so fast that she got frightened, and left her pitcher and ran for her life; she never stopped till she got to the top of a high hill; and when there, she turned about and saw the glen filled with water. Not a house or a field was to be seen! "Aha!" said she, "Tha Loch ann a nis." (Ha Loch an a neesh). There is a lake in it now; and so the lake was called Loch Ness (neesh).


Note that by clicking "GO!", you are starting up Amazon instances and volumes that will be charged to your account.
The mapping and variant calling for a whole genome (30X coverage) is estimated at about $30.
For a full exome (50X coverage), the cost is estimated at about $2.
However, note that these estimates do not include storage (S3) and that processing times (and thus costs) may vary.
When you click "Cancel", all of your Amazon instances will be terminated. Any jobs that are incomplete
will not be finished. You will no longer be charged for compute time on EC2.
Check the box below if you additionally would like your S3 bucket deleted (you will lose all your uploaded data and any other data in that bucket).


Week 4
Day 16 (9th Nov 2015)
From today onwards Mr Fransisco will be taking classes and training us while Luciano our first trainer will train the guys at Fablab Trivandrum. So it was swap between them. Frnsisco (alias Frank) reached in time. He set up his laptop and became ready to take the class.
He introduced the kokopelli an opensource tool the design the PCBs , an alternative to eagle which i was using for longtime.
He gave a link to download the source code. I downloaded it and extracted. There were some dependencies we should install, but it was already installed in my PC. So it was pretty simple.
Opened the terminal
change the directory to folder where it is extracted and typed the following commands
make clean
make fab
cd bin
./kokopelli -r
Now the kokopelli is opened. There were a sample code in it which was to draw a circle, but there were no circle as output.Something wrong!!
Then I noticed that there were no 'sudo' in the code, I tried with sudo.
sudo ./kokopelli -r
Ohh it works!!!
Eventhough i got the outup it was showing "root is not the owner of the file" in the terminal. It means there is no sudo needed for the kokopelli to run. Any way i went on with sudo since it was giving output.
Next i downloaded the ".cad" file made by Neil from http://academy.cba.mit.edu/classes/embedded_programming/index.html
I opened it in kokopelli. OMG!!! It was a code with 4000 lines. The functions for using almost the components in the fablab were defined in it. The chips , the resistors , wires , everything. A pcb for an isp programmer using Tiny44 is also there. So in the left side the code, and in the right side the pcb preview.
Frank showed us how to use it and how to add a component , how to allin it and how to connect it using wires. It was simple because all the functions for these things were already there.
I added an LED named led1 in the board which was already designed in kokopelli.The screens shots are here.
So we learnt to design a pcb using kokopelli. According to me, one problem with kokopelli as a pcb design tool is, the lack of autoroute. We have to join manually all the wires. If it is a complicated circuit then it is somewhat risk to draw the pcb manually. I think we should try to make a program for autoroute in kokopelli in future , when we get time.
Day 17 (10th Nov 2015)
Today we started with cleaning the lab. We cleaned all the tables and machine surroundings. Although we had done it last week , many things were not in place today. So we kept all the things in place.
Next, Frank asked to 3D print somthing to those who didnt do it already. Since i designed one and printed it last week itself i was doing some documentation when the other were printing.
What we plan to do today is making a small board with ATtiny and some LEDs for learning programming. Fransisco told we can make other boards also, its upto us. So i decided to make a fabduino.
All the details to make a fabduino is available at http://fab.cba.mit.edu/content/projects/fabkit/.
I downloaded all the schematic, board and the .img files.
Board, schematic.
The image files are traces, outline,and holes.
Since the machine(modella) is busy i have to wait. At that time i collected all the components, and made averything ready for milling the pcb.
Day 18 (11th Nov 2015)
Since i was ready with all the designs and components, i went to the modella machine directly when the fablab got opened. See the .png files for milling the PCB.
I have explained using the modella machiine several times. So i am not explaining it. The machine milled the PCB and cut it. See the bord.
Then i soldered the components.
Tha fabduino is ready.