Tag Archives: Azure

Organizational Accounts for ASP .NET Core 3.1

By Shahed C on April 13, 2020

This is the fifteenth of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

NOTE: The NetLearner suite of apps won’t be updated to use organizational authentication in the main branch, so you can check out the new sample code in the experimental subfolder, merged from a branch:

In this Article:

O is for Organizational Accounts

If you’ve created new ASP .NET Core projects, you’ve probably seen an option to add authentication upon creation. In Visual Studio, the IDE provides radio buttons to select a specific type of Authentication. Using CLI commands (e.g. in the VS Code terminal) you can use the ‐‐auth flag to choose the type of authentication you’d like to add.

The possible values are:

  • None – No authentication (Default).
  • Individual – Individual authentication (accounts stored in-app).
  • IndividualB2C – Individual authentication with Azure AD B2C.
  • SingleOrg – Organizational authentication for a single tenant.
  • MultiOrg – Organizational authentication for multiple tenants.
  • Windows – Windows authentication..

In this article, we will focus on the option for Work or School Accounts. This option can be used to authenticate users with AD (Active Directory, Azure AD or Office 365. This authentication process is illustrated in the diagram shown below:

ASP .NET Core web authentication via AzureAD
ASP .NET Core web authentication via AzureAD

Adding Authentication

To add authentication to a new project quickly, here are the instructions for Visual Studio 2019.

If you choose to use the new splash screen:

  1. Click “Create a new project”
  2. Select “ASP .NET Core Web Application” or “Blazor” (server-side)
  3. Click Next
  4. Enter Project Name, Location, Solution Name
  5. Optional: check the checkbox to place in the same directory
  6. Click Create
  7. Select .NET Core and then ASP .NET Core 3.1 from dropdowns
  8. Select a project type, e.g. Empty, Web Application (Razor Pages or MVC), Blazor App.
  9. Click the “Change” action link in the Authentication section
Creating a new web app project with authentication
Creating a new web app project with authentication

This should allow you to change the authentication type to “Work or School Accounts” so that you may your organizational domain info. As always, you may select the little info buttons (lowercase i) to learn more about each field. Talk to your system administrator if you need more help on what domains to use.

Work/School account selected for authentication method

NOTE: If you need help creating new .NET Core 3.1 project types in Visual Studio, take a look at this previous blog post in this series on .NET Core 3.0 to get some help on how to enable it.

If you wish to skip the Splash Screen instead upon launching VS2019:

  1. Click “Continue without code” in the lower right area of the splash screen.
  2. In the top menu, click File | New | Project (or Ctrl-Shift-N)
  3. Follow the remaining steps outlined earlier in this section

To use CLI Commands in a Terminal window, use the dotnet new command followed by the ‐‐auth flag. The authentication type can be any of the aforementioned authentication types, e.g. Individual.

dotnet new mvc --auth Individual -o myproj

Configuring App Registration

If you’ve used “Individual User Accounts” before, you’ve probably used a database to store user data, either on-premises or in the cloud. If you’ve used “Work or School Accounts” (i.e. organizational accounts), you may have the old App portal at the following URL:

Old App Registration Portal
Old App Registration Portal

On the old site, you may see a message suggesting that you should go to the Azure Portal to use the newer (current) App Registrations feature. This feature was previously in preview, and you can currently use it directly in the Azure Portal. If you click the link from the old site, it should take you directly to the App Registrations page, and may prompt you to log in first.

TODO: screenshot of new (preview?) portal, overview section
App Registration screen showing overview

If you’re not clear about how you got to this screen or how to come back to it later, here’s a set of steps that may help.

  1. Log in to the Azure Portal
  2. Search for App Registrations
  3. Arrive at the App Registrations page
  4. When a newer preview version becomes available, you may have to click the banner that takes you to a preview experience.
New App Registration in Azure Portal
New App Registration in Azure Portal

In the form that follows, fill out the values for:

  • Name (which you can change later)
  • Account Type (your org, any org, any org + personal MSA)
  • Redirect URI (where users will return after authentication)
authentication
Register an application for authentication

Now you should have options to configure your app and also download a pre-written application to get started. In the QuickStart section for your newly registered application (after selecting  the guide for “ASP .NET Core”, you should see a button to make the changes for you and also download a configured Visual Studio application.

Quickstart screen for app registration
Quickstart screen for app registration

In the steps that follow:

  1. Click the “Make the changes for me” button to make the necessary configuration changes.
  2. Optional: Click the “Download” link to download the pre-configured Visual Studio solution, if you don’t already have a project.

At the time of this writing, the downloadable sample project is a VS2019 application for ASP .NET Core 2.2. You can download it to inspect it, but I would recommend creating a new project manually in VS2019. There may be some subtle differences between projects created by VS2019 with authentication turned on, versus what you get with the downloaded project.

For further customization using the Manifest file available to you, check out the official documentation on the Azure AD app manifest:

Using Authentication in Your Code

When creating a new project in VS2019, you get the following lines of code in your ConfigureServices() method, including calls to .AddAuthentication(). For different project types (MVC, Razor Pages, Blazor), you should also see some additional code setting up the authorization policy.

// contents of ConfigureServices() when created in VS2019

services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));

The string value “AzureAd” is referenced in your appsettings file, e.g. appsettings.json in MVC, Razor Pages, or Blazor projects. The properties for this entry include the following:

  • Instance: URL for login page, e.g. https://login.microsoftonline.com
  • Domain: your AD tenant’s domain, e.g. microsoft.onmicrosoft.com
  • TenantId: your Tenant Id value, usually a GUID
  • ClientId: Your app’s Client Id, obtained from app registration
  • Callback Path: partial path for sign-in, e.g. /signin-oidc
{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<REPLACE_DOMAIN_NAME>",
    "TenantId": "<REPLACE_TENANT_ID>",
    "ClientId": "<REPLACE_CLIENT_ID>",
    "CallbackPath": "/signin-oidc"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

You can see more information on appsettings.json values in the official docs. You can also open the apssettings.json from the portal-downloaded project to get your own app’s Id values. The following documentation is specifically written for v2, but offers explains for important fields such as ClientId and TenantId.

To get a refresher on Authentication and Authorization in ASP .NET Core 3.1, check out the following post on from earlier in this blog series.

For more on assigning users to specific roles, check out the official documentation at the following URL:

Finally, take a look at the Login.partial.cshtml partial view to observe the way a user’s identity is detected and shown in an MVC or Razor Pages web app. Here is a snippet from the samples:

@if (User.Identity.IsAuthenticated)
{
 <li class="nav-item">
 <span class="nav-text text-dark">Hello @User.Identity.Name!</span>
 </li>
...
}

NOTE: The Blazor web app wraps the authorized content in an <Authorized> tag within an <AuthorizedView. For more on the Blazor project, take a look at the Blazor post:

Depending on what you have access to, the User.Identity object may not contain everything you expect it to. Here are some things to take note of:

  • User.Identity should be null when not logged in
  • User.Identity should be non-null when logged in…
  • … however, User.Identity.Name may be null even when logged in
  • If User.Identity.Name is null, also check User.Identity.Claims
  • User.Identity.Claims should have more than 0 values when logged in

The following screenshot shows an example of the user information in my debugging environment when logged in:

 User.Identity.Name with Claims in debugger
User.Identity.Name with Claims in debugger

Running the Samples

Running the sample web projects should redirect you to the Azure AD login page for your tenant. The login page should look identical for all 3 project types: MVC, Razor Pages or Blazor. If you’ve already logged in to one or more accounts in your browser, you should have one or more identities to select from.

Once you’ve selected an account, you should see a confirmation screen (at least the first time) to Accept the authentication and login. This screen will look similar for all your projects, and the project name should be displayed in the middle.

 MVC app authentication confirmation
MVC app authentication confirmation
Razor Pages app authentication confirmation
Blazor app authentication confirmation
Blazor app authentication confirmation

Finally, you should see each web application with some indication that you are currently logged in. In the template-generated sample apps, this is demonstrated by the “Hello” message in the header area.

MVC web app, logged in
MVC web app, logged in
Razor Pages web app, logged in
Razor Pages web app, logged in
 Blazor web app, logged in
Blazor web app, logged in

References

Logging in ASP .NET Core 3.1

By Shahed C on March 24, 2020

This is the twelfth of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

L is for Logging in ASP .NET Core

You could write a fully functional ASP .NET Core web application without any logging. But in the real world, you should use some form of logging. This blog post provides an overview of how you can use the built-in logging functionality in ASP .NET Core web apps. While we won’t go deep into 3rd-party logging solutions such as Serilog in this article, you should definitely consider a robust semantic/structured logging solution for your projects.

Logging providers in ASP .NET Core 3.1

Log Messages

The simplest log message includes a call to the extension method ILogger.Log()  by passing on a LogLevel and a text string. Instead of passing in a LogLevel, you could also call a specific Log method such as LogInformation() for a specific LogLevel. Both examples are shown below:

// Log() method with LogLevel passed in 
_logger.Log(LogLevel.Information, "some text");

// Specific LogXX() method, e.g. LogInformation()
_logger.LogInformation("some text");

LogLevel values include Trace, Debug, Information, Warning, Error, Critical and None. These are all available from the namespace Microsoft.Extensions.Logging. For a more structured logging experience, you should also pass in meaningful variables/objects following the templated message string, as all the Log methods take in a set of parameters defined as “params object[] args”.

public static void Log (
   this ILogger logger, LogLevel logLevel, string message, params object[] args);

This allows you to pass those values to specific logging providers, along with the message itself. It’s up to each logging provider on how those values are captured/stored, which you can also configure further. You can then query your log store for specific entries by searching for those arguments. In your code, this could look something like this:

_logger.LogInformation("some text for id: {someUsefulId}", someUsefulId);

Even better, you can add your own EventId for each log entry. You can facilitate this by defining your own set of integers, and then passing an int value to represent an EventId. The EventId type is a struct that includes an implicit operator, so it essentially calls its own constructor with whatever int value you provide.

_logger.LogInformation(someEventId, "some text for id: {someUsefulId}", someUsefulId);

In the NetLearner.Portal project, we can see the use of a specific integer value for each EventId, as shown below:

// Step X: kick off something here
_logger.LogInformation(LoggingEvents.Step1KickedOff, "Step {stepId} Kicked Off.", stepId);

// Step X: continue processing here
_logger.LogInformation(LoggingEvents.Step1InProcess, "Step {stepId} in process...", stepId);

// Step X: wrap it up
_logger.LogInformation(LoggingEvents.Step1Completed, "Step {stepId} completed!", stepId);

The integer values can be whatever you want them to be. An example is shown below:

public class LoggingEvents
{
   public const int ProcessStarted = 1000; 

   public const int Step1KickedOff = 1001;
   public const int Step1InProcess = 1002;
   public const int Step1Completed = 1003; 
   ...
}

Logging Providers

The default template-generated web apps include a call to CreateDefaultBuilder() in Program.cs, which automatically adds the Console and Debug providers. As of ASP.NET Core 2.2, the EventSource provider is also automatically added by the default builder. When on Windows, the EventLog provider is also included.

 public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(webBuilder =>
      {
         webBuilder.UseStartup<Startup>();
      });

NOTE: As mentioned in an earlier post in this blog series, the now-deprecated Web Host Builder has been replaced by the Generic Host Builder with the release of .NET Core 3.0.

If you wish to add your own set of logging providers, you can expand the call to CreateDefaultBuilder(), clear the default providers, and then add your own. The built-in providers now include ConsoleDebug, EventLog, TraceSource and EventSource.

public static IHostBuilder CreateHostBuilder(
   string[] args) => Host.CreateDefaultBuilder(args)  
      .ConfigureWebHostDefaults(webBuilder =>
      {
         webBuilder.UseStartup<Startup>();
      })
      .ConfigureLogging(logging =>  
      {  
         // clear default logging providers
         logging.ClearProviders();  

         // add built-in providers manually, as needed 
         logging.AddConsole();   
         logging.AddDebug();  
         logging.AddEventLog();
         logging.AddEventSourceLogger();
         logging.AddTraceSource(sourceSwitchName); 
      });

The screenshots below show the log results viewable in Visual Studio’s Debug Window and in the Windows 10 Event Viewer. Note that the EventId’s integer values (that we had defined) are stored in the EventId field as numeric value in the Windows Event Viewer log entries.

 VS2019 Output panel showing debug messages
VS2019 Output panel showing debug messages
Windows Event Viewer showing log data

For the Event Log provider, you’ll also have to add the following NuGet package and corresponding using statement:

Microsoft.Extensions.Logging.EventLog

For the Trace Source provider, a “source switch” can be used to determine if a trace should be propagated or ignored. For more information on the Trace Source provider and the Source Switch it uses check out the official docs at:

For more information on adding logging providers and further customization, check out the official docs at:

JSON Configuration

One way to configure each Logging Provider is to use your appsettings.json file. Depending on your environment, you could start with appsettings.Development.json or App Secrets in development, and then use environment variables, Azure Key Vault in other environments. You may refer to earlier blog posts from 2018 and 2019 for more information on the following:

In your local JSON config file, your configuration uses the following syntax:

{
   "Logging": {
      "LogLevel": {
         "Default": "Debug",
         "Category1": "Information",
         "Category2": "Warning"
      },
      "SpecificProvider":
      {
         "ProviderProperty": true
      }
   }
}

The configuration for LogLevel sets one or more categories, including the Default category when no category is specified. Additional categories (e.g. System, Microsoft or any custom category) may be set to one of the aforementioned LogLevel values.

The LogLevel block can be followed by one or more provider-specific blocks (e.g. Console) to set its properties, e.g. IncludeScopes. Such an example is shown below.

{
   "Logging": {
      "LogLevel": {
         "Default": "Debug",
         "System": "Information",
         "Microsoft": "Information"
      },
      "Console":
      {
         "IncludeScopes": true
      }
   }
}

To set logging filters in code, you can use the AddFilter () method for specific providers or all providers in your Program.cs file. The following syntax can be used to add filters for your logs.

  .ConfigureLogging(logging =>
      logging.AddFilter("Category1", LogLevel.Level1)
         .AddFilter<SomeProvider>("Category2", LogLevel.Level2));

In the above sample, the following placeholders can be replaced with:

  • CategoryX: System, Microsoft, custom categories
  • LogLevel.LevelX: Trace, Debug, Information, Warning, Error, Critical, None
  • SomeProvider: Debug, Console, other providers

To set the EventLog level explicitly, add a section for “EventLog” with the default minimum “LogLevel” of your choice. This is useful for Windows Event Logs, because the Windows Event Logs are logged for Warning level or higher, by default.

{
   "Logging": {
     "LogLevel": {
       "Default": "Debug"
     },
     "EventLog": {
       "LogLevel": {
         "Default": "Information"
       }
     }
   }
 }

Log Categories

To set a category when logging an entry, you may set the string value when creating a logger. If you don’t set a value explicitly, the fully-qualified namespace + class name is used. In the WorkhorseModel class seen in The NetLearner.Portal project, the log results seen in the Debug window started with:

NetLearner.Portal.Pages.WorkhorseModel

This is the category name created using the class name passed to the constructor in WorkhorseModel as shown below:

private readonly ILogger _logger; 

public WorkhorseModel(ILogger<WorkhorseModel> logger)
{
   _logger = logger;
}

If you wanted to set this value yourself, you could change the code to the following:

private readonly ILogger _logger; 

public WorkhorseModel(WorkhorseModel logger)
{
   _logger = logger.CreateLogger("NetLearner.Portal.Pages.WorkhorseModel");
}

The end results will be the same. However, you may notice that there are a couple of differences here:

  1. Instead of ILogger<classname> we are now passing in an ILoggerFactory type as the logger.
  2. Instead of just assigning the injected logger to the private _logger variable, we are now calling the factory method CreateLogger() with the desired string value to set the category name.

Exceptions in Logs

In addition to EventId values and Category Names, you may also capture Exception information in your application logs. The various Log extensions provide an easy way to pass an exception by passing the Exception object itself.

try 
{
   // try something here
   throw new Exception();
} catch (Exception someException)
{
   _logger.LogError(eventId, someException, "Trying step {stepId}", stepId);
   // continue handling exception
}

Checking the Event Viewer, we may see a message as shown below. The LogLevel is shown as “Error” because we used the LogError() extension method in the above code, which is forcing an Exception to be thrown. The details of the Exception is displayed in the log as well.

Windows Event Viewer showing error log entry
Windows Event Viewer showing error log entry

Structured Logging with Serilog

At the very beginning, I mentioned the possibilities of structured logging with 3rd-party providers. There are many solutions that work with ASP .NET Core, including (but not limited to) elmahNLog and Serilog. Here, we will take a brief look at Serilog.

Similar to the built-in logging provider described throughout this article, you should include variables to assign template properties in all log messages, e.g.

Log.Information("This is a message for {someVariable}", someVariable);

To make use of Serilog, you’ll have to perform the following steps:

  1. grab the appropriate NuGet packages: SerilogHosting, various Sinks, e,g, Console
  2. use the Serilog namespace, e.g. using Serilog
  3. create a new LoggerConfiguration() in your Main() method
  4. call UseSerilog() when creating your Host Builder
  5. write log entries using methods from the Log static class.

For more information on Serilog, check out the following resources:

References

Key Vault for ASP .NET Core 3.1 Web Apps

By Shahed C on March 16, 2020

This is the eleventh of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

K is for Key Vault for ASP .NET Core 3.1 Web Apps

In my 2018 blog series, we covered the use of User Secrets for your ASP .NET Core web application projects. This is useful for storing secret values locally during development. However, we need a cloud-based scalable solution when deploying web apps to Azure. This article will cover Azure Key Vault as a way to store and retrieve sensitive information in Azure and access them in your web application.

You will need an Azure subscription to create and use your own Key Vault and App Service.

Using Key Vault from Azure for your Web Apps
Using Key Vault from Azure for your Web Apps

Setting up Key Vault in Azure

Before you can use Key Vault in the cloud, you will have to set it up in Azure for your use. This can be done in various ways:

  • Azure Portal: log in to the Azure Portal in a web browser.
  • Azure CLI: use Azure CLI commands on your development machine.
  • Visual Studio: use the VS IDE on your development machine.

To use the Azure Portal: create a new resource, search for Key Vault, click Create and then follow the onscreen instructions. Enter/select values for the following for the key vault:

  • Subscription: select the desired Azure subscription
  • Resource Group: select a resource group or create a new one
  • Name: alphanumeric, dashes allowed, cannot start with number
  • Region: select the desired region
  • Pricing Tier: select the appropriate pricing tier (Standard, Premium)
  • Soft Delete: enable/disable
  • Retention Period: enter retention period (in days)
  • Purge protection: enable/disable
Key Vault creation screen on Azure Portal
Key Vault creation screen on Azure Portal

If you need help with the Azure Portal, check out the official docs at:

To use the Azure CLI: authenticate yourself, run the appropriate commands to create a key vault, add keys/secrets/certificates and then authorize an application to use your keys/secrets.

To create a new key vault, run “az keyvault create” followed by a name, resource group and location, e.g.

az keyvault create --name "MyKeyVault" --resource-group "MyRG" --location "East US"

To add a new secret, run “az keyvault secret set“, followed by the vault name, a secret name and the secret’s value, e.g.

az keyvault secret set --vault-name "MyKeyVault" --name "MySecretName" --value "MySecretValue"

If you need help with the Azure CLI, check out the official docs at:

To use Visual Studio, right-click your project in Solution Explorer, click Add | Connected Service, select “Secure Secrets with Azure Key Vault” and follow the onscreen instructions.

If you need help with Visual Studio, check out the official docs at:

Add a Connected Service in Visual Studio
Add a Connected Service in Visual Studio

Once created, the Key Vault and its secret names (and values) can be browsed in the Azure Portal, as seen in the screenshots below:

Key Vault details
Key Vault creation in Azure Portal
Key Vault creation in Azure Portal
Creating a secret for Azure Key Vault
Creating a secret for Azure Key Vault

NOTE: If you create a secret named “Category1–MySecret”, this syntax specifies a category “Category1” that contains a secret “MySecret.”

Retrieving Key Vault Secrets

Before you deploy your application to Azure, you can still access the Key Vault using Visual Studio during development. This is accomplished by using your Visual Studio sign-in identity. Even if you have multiple levels of configuration to retrieve a secret value, the app will use the config sources in the following order:

  • first, check the Key Vault.
  • if Key Vault not found, check secrets.json file
  • finally, check the appsettings.json file.

There are a few areas in your code you need to update, in order to use your Key Vault:

  1. Install the nuget packages AppAuthentication, KeyVault and AzureKeyVault NuGet libraries.
    • Microsoft.Azure.Services.AppAuthentication
    • Microsoft.Azure.KeyVault
    • Microsoft.Extensions.Configuration.AzureKeyVault
  2. Update Program.cs to configure your application to use Key Vault
  3. Inject an IConfiguration object into your controller (MVC) or page model (Razor Pages, shown below)
  4. Access specific secrets using the IConfiguration object, e.g. _configuration[“MySecret”]

Below is an example of Program.cs using the WebHostBuilder’s ConfigureAppConfiguration() method to configure Key Vault. The keyVaultEndpoint is the fully-qualified domain name of your Key Vault that you created in Azure.

...
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureKeyVault;
...
public static IHostBuilder CreateHostBuilder(string[] args) =>
   Host.CreateDefaultBuilder(args)
      .ConfigureAppConfiguration((ctx, builder) =>
      {
         var keyVaultEndpoint = GetKeyVaultEndpoint();
         if (!string.IsNullOrEmpty(keyVaultEndpoint))
         {
            var azureServiceTokenProvider = new AzureServiceTokenProvider();
            var keyVaultClient = new KeyVaultClient(
               new KeyVaultClient.AuthenticationCallback(
                  azureServiceTokenProvider.KeyVaultTokenCallback));
               builder.AddAzureKeyVault(
                  keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
         }
      }
   ).ConfigureWebHostDefaults(webBuilder =>
   {
      webBuilder.UseStartup<Startup>();
   }); 

private static string GetKeyVaultEndpoint() => "https://<VAULT_NAME>.vault.azure.net/";

NOTEPlease note that usage of Web Host Builder in ASP .NET Core 2.x has been replaced by the Generic Host Builder in .NET Core 3.x.

Below is an example of of a Page Model for a Razor Page, e.g. SecretPage.cshtml.cs in the sample app

...
using Microsoft.Extensions.Configuration;
public class SecretPageModel : PageModel
{
   public IConfiguration _configuration { get; set; }
   public string Message1 { get; set; }
   public string Message2 { get; set; }
   public string Message3 { get; set; } 

   public SecretPageModel(IConfiguration configuration)
   {
      _configuration = configuration;
   }

   public void OnGet()
   {
      Message1 = "My 1st key val = " + _configuration["MyKeyVaultSecret"];
      Message2 = "My 2nd key val = " + _configuration["AnotherVaultSecret"];
      Message3 = "My 3nd key val ? " + _configuration["NonExistentSecret"];
   }
}

In the above code, the configuration object is injected into the Page Model’s SecretPageModel()The values are retrieved from the collection in the OnGet() method and assigned to string properties. In the code below, the string properties are accessed from the model class in the corresponding Razor Page, SecretPage.cshtml.

@page
@model KeyVaultSample.Pages.SecretPageModel
...
<p>
 @Model.Message1
</p> 

<p>
 @Model.Message2
</p> 

<p>
 @Model.Message3
</p>

Finally, viewing the page allows you to navigate to the Secret Page to view the secret values. Note that I’ve only created 2 secret values before deploying this instance, so the 3rd value remains blank (but without generating any errors).

 SecretPage showing secrets retrieved from Key Vault
SecretPage showing secrets retrieved from Key Vault

Managed Service Identity

There are multiple ways to deploy your ASP .NET Core web app to Azure, including Visual Studio, Azure CLI or a CI/CD pipeline integrated with your source control system. If you need help deploying to Azure App Service, check out the following article from this blog series:

You can set up your Managed Service Identity in various ways:

  • Azure Portal: log in to the Azure Portal and add your app
  • Azure CLI: use Azure CLI commands to set up MSI
  • Visual Studio: use the VS IDE while publishing

Once you’ve created your App Service (even before deploying your Web App to it), you can use the Azure Portal to add your app using Managed Service Identity. In the screenshots below, I’ve added my sample app in addition to my own user access.

  • In the Access Policies section of the Key Vault, you may add one or more access policies.
  • In the Identity section of the App Service, you may update the System-Assigned setting to “On” and make a note of the Object ID, which is defined as a “Unique identifier assigned to this resource, when it’s registered with Azure Active Directory
Key Vault Access Policies
Key Vault Access Policies
Key Vault App Service Identity
Key Vault App Service Identity

To use the Azure CLI to authorize an application to access (or “get”) a key vault, run “az keyvault set-policy“, followed by the vault name, the App ID and specific permissions. This is equivalent to enabling the Managed Service Identity for your Web App in the Azure Portal.

az keyvault set-policy --name "MyKeyVault" --spn <APP_ID> --secret-permissions get

To use Visual Studio to use your key vault after deployment, take a look at the Publish screen when deploying via Visual Studio. You’ll notice that there is an option to Add Dependencies, including Azure Key Vault. After you’ve added and enabled Key Vault for your application, the option will change to say “Configure” and “Manage Key Vault”.

Add Dependencies during Publish from Visual Studio
Add Dependencies during Publish from Visual Studio
Select Azure Key Vault from list of dependencies
Select Azure Key Vault from list of dependencies

After adding via Visual Studio during the Publish process, your Publish Profile (profile – Web Deploy.pubxml) and Launch Settings profiles (launchSettings.json) should contain the fully qualified domain name for your Key Vault in Azure. You should not include these files in your source control system.

References

Deploying ASP .NET Core 3.1 to Azure App Service

By Shahed C on January 27, 2020

This is the fourth of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

D is for Deploying to Azure App Service

In this article, we’ll explore several options for deploying an ASP .NET Core web app to Azure App Service in the cloud. From the infamous Right-Click-Publish to fully automated CI/CD, you’ll learn about the latest Deployment Center option in the Azure Portal for App Service for web apps.

NOTE: If you’re looking for information on deploying to Docker or Kubernetes, please check out the following docs instead:

Right-Click Publish (aka Friends Don’t Let Friends Right-Click Publish)

If you’ve made it this far, you may be thinking one of the following:
a. “Hey, this is how I deploy my web apps right now!”
or
b. “Hey wait a minute, I’ve heard that you should never do this!”

Well, there is a time and place for right-click publish. There have been many debates on this, so I won’t go into the details, but here are some resources for you to see what others are saying:

So, what’s a web developer to do? To quote from the aforementioned MSDN article, “Continuing with the theme of prototyping and experimenting, right click publish is the perfect way for existing Visual Studio customers to evaluate Azure App Service (PAAS). By following the right click publish flow you get the opportunity to provision new instances in Azure and publish your application to them without leaving Visual Studio:”

In other words, you can use this approach for a quick test or demo, as shown in the screenshots below for Visual Studio.

  1. Right-click your ASP .NET Core web app project in Solution Explorer and select Publish.
  2. Click the Start button on the screen that appears and follow the onscreen instructions.
  3. Ensure that you’re logged in to the correct Azure subscription account you want to publish to.
Right-click, Publish from Solution Explorer
Right-click, Publish from Solution Explorer
Pick a Publish Target
Pick a Publish Target

Web Apps in the Azure Portal

In the screenshot above, you may notice an option to “Import Profile” using the button on the lower left. This allows you to import a Web App profile file that was generated by exporting it from an existing Azure Web App. To grab this profile file, simply navigate to your existing Web App in the Azure Portal and click on “Get publish profile” in the top toolbar of your Web App, shown below:

Get Publish Profile
Get Publish Profile

If you want to create a new Web App in Azure starting with the Azure Portal, follow the instructions below:

  1. Log in to the Azure at https://portal.azure.com
  2. On the top left, click + Create a resource
  3. Select “Web App” or search for it if you don’t see it.
  4. Enter/select the necessary values:
    • Subscription (select a subscription)
    • Resource Group (create or use existing to group resources logically)
    • Web App name (enter a unique name)
    • Publish (Code or Docker Image)
    • Runtime stack (.NET Core)
    • App Service Plan (create or use existing to set location and pricing tier)
    • OS (Windows or Linux)
    • Region (e.g. East US)
  5. Click Next through the screens and then click the Create button to complete the creation of your new Web App.
Create New Web App
Create New Web App

Now you can deploy to this Web App using any method you choose.

Runtime Options

If you like to stay ahead of ASP .NET Core releases, you may be using a pre-release version of the runtime. As of this writing, the latest stable version of ASP .NET Core is version 3.1, which is already available on Azure. If you’re looking for future preview releases, Azure App Service also has an option to install an Extension for preview runtimes.

To find the proper runtime:

  1. Navigate to your Web App in the Azure Portal.
  2. Click on Extensions under Development Tools.
  3. Click + Add to add a new extension.
  4. Choose an extension to configure required settings.
  5. Accept the legal terms and complete the installation.
Add Extension
Add Extension

Your options may include both 32-bit (x86) and 64-bit (x64) versions of the ASP .NET Core runtime and any preview versions of future releases. When planning ahead for multiple environments, you also have the option to deploy to Deployments Slots. This feature is available in StandardPremium or Isolated App Service Plan tiers and will covered in a future blog post in this series.

If you’re interested in Deployment Slots right now, check out the official docs at:

Deployment Center

In the list of features for your Web App, you will find an option to open up the new Deployment Center. Note that this has replaced the old Deployment Options. Let’s go over each of these options:

  1. Azure Repos
  2. Github
  3. Bitbucket
  4. Local Git
  5. OneDrive
  6. Dropbox
  7. External
  8. FTP
Deployment Options in Deployment Center
Deployment Options in Deployment Center
More Deployment Options
More Deployment Options

If you choose Azure Repos, you can set up your web app’s CI (Continuous Integration) system with an Azure Repo, which is part of Microsoft’s Azure DevOps services (formerly known as VSTS, aka Visual Studio Team Services). You will have options for using App Service as a Kudu build server or Azure Pipelines as your CI build system.

 Azure Repos choices: Kudu or Pipelines?
Azure Repos choices: Kudu or Pipelines?

If you choose Github or BitBucket or even a local Git account, you’ll have the ability to authorize that account to publish a specific repo, every time a developer pushes their code.

 Authorize Github/Bitbucket
Authorize Github/Bitbucket

If you choose OneDrive or DropBox, you’ll have ability to authorize your App Service to pick up files deployed to a shared folder in either location.

 Authorize OneDrive/DropBox
Authorize OneDrive/DropBox

You may also select an External repo or FTP source. To learn more about Azure Repos and Azure Pipelines, check out the official docs:

GitHub Repos (includes FREE option!)

If you’ve been using GitHub for public open-source projects or private projects on paid accounts, now is a great to time to create private repositories for free! In 2019, GitHub started offering free unlimited private repos, limited to 3 collaborators. This new free option comes with issue/bug tracking and project management as well.

For more information on GitHub pricing, check out their official pricing page:

 GitHub pricing: Free vs Pro, Team and Enterprise
GitHub pricing: Free vs Pro, Team and Enterprise

Now you can easily set up your starter projects in a private GitHub repository and take advantage of the aforementioned CI/CD setup without having to choose between paying a GitHub fee or making all your repos public.

CLI Commands

If you wish to publish to Azure App service using CLI (Command Line Interface) Commands, you may use the following commands, where you can choose a name for your Web App, Resource Group, App Sevice Plan, etc. Single-letter flags are usually preceded by a single hyphen, while flags spelled out with completed words are usually preceded by two hyphens.

First, install the Azure CLI in case you don’t have it already:

Authenticate yourself:

> az login

Create a new resource group:

> az group create -l <LOCATION> -n <RSG>
> az group create --location <LOCATION> --name <RSG>

Create a new App Service Plan, where <SKUCODE> sku may be F1 or FREE, etc

> az appservice plan create -g <RSG> -n <ASP> --sku <SKUCODE> 
> az appservice plan create --resource-group <RSG> --name <ASP> --sku <SKUCODE>

From the documentation, the SKU Codes include: F1(Free), D1(Shared), B1(Basic Small), B2(Basic Medium), B3(Basic Large), S1(Standard Small), P1V2(Premium V2 Small), PC2 (Premium Container Small), PC3 (Premium Container Medium), PC4 (Premium Container Large), I1 (Isolated Small), I2 (Isolated Medium), I3 (Isolated Large).

Create a new Web App within a Resource Group, attached to an App Service Plan:

> az webapp create -g <RSG> -p <ASP> -n <APP> 
> az webapp create --resource-group <RSG> --plan <ASP> --name <APP>

The above command should create a web app available at the following URL:

  • http://<APP>.azurewebsites.net

To push your commits to a Git Repo and configure for App Service Deployment, use the following CLI commands:

Create a new Git repo or reinitialize an existing one:

> git init

Add existing files to the index:

> git add .

Commit your changes with a commit message:

> git commit -m "<COMMIT MESSAGE>“

Set your FTP credentials and Git deployment credentials for your Web App:

> az webapp deployment user set --user-name <USER>

Configure an endpoint and add it as a git remote.

> az webapp deployment source config-local-git -g <RSG> -n <APP> --out tsv  
> az webapp deployment source config-local-git --resource-group <RSG> --name <APP> --out tsv
> git remote add azure <GIT URL>

The value for GIT URL is the value of the Git remote, e.g.

  • https://<USER>@<APP>.scm.azurewebsites.net/<APP>.git

Finally, push to the Azure remote to deploy your Web App:

> git push azure master

For more information on CLI Commands for Git and Azure App Service, check out the official docs:

Azure DevOps and YAML

Wait, what about Azure DevOps and YAML and Pipelines?

Since this is an A-Z series, you will have to wait for “Y is for YAML” to get more detailed information on constructing your build pipeline using YAML in Azure DevOps. If you can’t wait that long, feel free to check out the following .yml sample I uploaded for use with an ASP .NET Core 3.1:

If you’re reading this after June 2020, simply jump right to the “Y is for YAML” post in the 2020 A-Z series.

BONUS: for Azure SQL Database Deployment, watch the following video on MSDN Channel 9:

References

Cookies and Consent in ASP .NET Core 3.1

By Shahed C on January 20, 2020

This is the third of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

C is for Cookies and Consent

In this article, we’ll continue to look at the (in-progress) NetLearner application, which was generated using multiple ASP .NET Core web app project (3.1) templates. In previous releases, the template made it very easy for you to store cookies and display a cookie policy. However, the latest version doesn’t include cookie usage or a GDPR-compliant message out of the box.

Unless you’ve been living under a rock in the past couple of years, you’ve no doubt noticed all the GDPR-related emails and website popups since 2018. Whether or not you’re required by law to disclose your cookie policies, it’s good practice to reveal it to the end user so that they can choose to accept your cookies (or not).

Who Moved My Cookies?

In ASP .NET Core 2.x, the standard web app project templates provided by Microsoft included GDPR-friendly popup messages, that could be accepted by the end user to set a consent cookie. As of ASP .NET Core 3.x, this is no longer provided out of the box. However, you can still add this feature back into your project manually if needed.

Follow the instructions provided in the official docs to add this feature to your ASP .NET MVC or Razor Pages project:

But wait… how about Blazor web app projects? After asking a few other developers on Twitter, I decided to implement this feature myself, in my NetLearner repository. I even had the opportunity to answer a related question on Stack Overflow. Take a look at the following Blazor web project:

Browser Storage

As you probably know, cookies are attached to a specific browser installation and can be deleted by a user at an any time. Some  new developers may not be aware of where these cookies are actually stored.

Click F12 in your browser to view the Developer Tools to see cookies grouped by website/domain.

  • In (pre-Chromum) Edge/Firefox, expand Cookies under the Storage tab.
  • In (Chromium-based) Edge/Chrome, expand Storage | Cookies under the Application tab .

See screenshots below for a couple of examples how AspNet.Consent in stored, along with a boolean Yes/No value:

Cookies in Chromium-based Edge
Cookies in Chromium-based Edge
Cookies in Google Chrome
 Cookies in Mozilla Firefox
Cookies in Mozilla Firefox

Partial Views for your cookie message

The first time you launch a new template-generated ASP .NET Core 2.x web app, you should expect to see a cookie popup that appears on every page that can be dismissed by clicking Accept. Since we added it manually in our 3.x project, let’s explore the code to dig in a little further.

GDPR-compliant cookie message
GDPR-compliant cookie message

First, take a look at the _CookieConsentPartial.cshtml partial view in both the Razor Pages shared pages folder and the MVC shared views folder. The CSS class names and accessibility-friendly role attributes have been removed for brevity in the snippet below. For Razor Pages (in this example), this file should be in the /Pages/Shared/ folder by default. For MVC, this file should be in the /Views/Shared/ folder by default.

@using Microsoft.AspNetCore.Http.Features

@{
    var consentFeature = Context.Features.Get<ITrackingConsentFeature>();
    var showBanner = !consentFeature?.CanTrack ?? false;
    var cookieString = consentFeature?.CreateConsentCookie();
}

@if (showBanner)
{
    <div id="cookieConsent">
        <!-- CUSTOMIZED MESSAGE IN COOKIE POPUP --> 
        <button type="button" data-dismiss="alert" data-cookie-string="@cookieString">
            <span aria-hidden="true">Accept</span>
        </button>
    </div>
    <script>
        (function () {
            var button = document.querySelector("#cookieConsent button[data-cookie-string]");
            button.addEventListener("click", function (event) {
                document.cookie = button.dataset.cookieString;
            }, false);
        })();
    </script>
} 

This partial view has a combination of server-side C# code and client-side HTML/CSS/JavaScript code. First, let’s examine the C# code at the very top:

  1. The using statement at the top mentions the Microsoft.AspNetCore.Http.Features namespace, which is necessary to use ITrackingConsentFeature.
  2. The local variable consentFeature is used to get an instance ITrackingConsentFeature (or null if not present).
  3. The local variable showBanner is used to store the boolean result from the property consentFeature.CanTrack to check whether the user has consented or not.
  4. The local variable cookieString is used to store the “cookie string” value of the created cookie after a quick call to consentFeature.CreateConsentCookie().
  5. The @if block that follows only gets executed if showBanner is set to true.

Next, let’s examine the HTML that follows:

  1. The cookieConsent <div> is used to store and display a customized message for the end user.
  2. This <div> also displays an Accept <button> that dismisses the popup.
  3. The  data-dismiss attribute ensures that the modal popup is closed when you click on it. This feature is available because we are using Bootstrap in our project.
  4. The data- attribute for “data-cookie-string” is set using the server-side variable value for @cookieString.

The full value for cookieString may look something like this, beginning with the .AspNet.Consent boolean value, followed by an expiration date.

".AspNet.Consent=yes; expires=Mon, 18 Jan 2021 21:55:01 GMT; path=/; secure; samesite=none"

Finally, let’s examine the JavaScript that follows within a <script> tag:

  1. Within the <script> tag, an anonymous function is defined and invoked immediately, by ending it with (); after it’s defined.
  2. button variable is defined to represent the HTML button, by using an appropriate querySelector to retrieve it from the DOM.
  3. An eventListener is added to respond to the button’s onclick event.
  4. If accepted, a new cookie is created using the button’s aforementioned cookieString value.

To use the partial view in your application, simply insert it into the _Layout.cshtml page defined among both the Razor Pages shared pages folder and the MVC shared views folder. The partial view can be inserted above the call to RenderBody() as shown below.

<div class="container">
   <partial name="_CookieConsentPartial" />
   <main role="main" class="pb-3">
      @RenderBody()
   </main>
</div>

In an MVC application, the partial view can be inserted the same way, using the <partial> tag helper.

Blazor Implementation

Here are the steps I used in the Blazor project, tracked in the BlazorCookieExperiment branch:

  1. Update ConfigureServices() in Startup.cs to set up cookie usage
  2. Use JSInterop to set document.cookie in netLearnerJSInterop.js
  3. Update _Host.cshtml to include the .js file
  4. Observe code in _CookieConsentPartial.cshtml as reference
  5. Add _CookieConsentPartial.razor component in Shared folder
  6. Update MainLayout.razor to include above component
<div class="main">
    <div class="top-row px-4 auth">
        <LoginDisplay />
        <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
    </div>

    <_CookieConsentPartial />
    <div class="content px-4">
        @Body
    </div>
</div> 

Customizing your message

You may have noticed that there is only an Accept option in the default cookie popup generated by the template’s Partial View. This ensures that the only way to store a cookie with the user’s consent is to click Accept in the popup.

You may be wondering whether you should also display a Decline option in the cookie popup. But that would be a bad idea, because that would require you to store the user’s “No” response in the cookie itself, thus going against their wishes. If you wish to allow the user to withdraw consent at a later time, take a look at the GrantConsent() and WithdrawConsent() methods provided by ITrackingConsentFeature.

But you can still change the message in the cookie popup and your website’s privacy policy. To change the cookie’s displayed message, simply change the text that appears in the _CookieConsentPartial.cshtml partial view (or equivalent Razor component for ther Blazor project), within the <div> of the client-side HTML. In the excerpt shown in the previous section, this region is identified by the <!– CUSTOMIZED MESSAGE IN COOKIE POPUP –> placeholder comment.

   <div id="cookieConsent">
      <!-- CUSTOMIZED MESSAGE IN COOKIE POPUP -->
      <button type="button" data-dismiss="alert" data-cookie-string="@cookieString">
         <span aria-hidden="true">Accept</span>
      </button>
   </div>

Your message text is also a great place to provide a link to your website’s privacy policy. In the Razor Pages template, the <a> link is generated using a tag helper shown below. The /Privacy path points to the Privacy.cshtml Razor page in the /Pages folder.

<a asp-page="/Privacy">Learn More</a>

In a similar MVC application, you would find the Privacy.cshtml view within the /Views/Home/ folder, accessible via the Home controller’s Privacy() action method. In the MVC template, the <a> is link is generated using the following tag helper:

<a asp-area="" asp-controller="Home" asp-action="Privacy">Learn More</a>

Startup Configuration

None of the above would be possible without the necessary configuration. The cookie policy can be used by simply calling the extension method app.UseCookiePolicy() in the Configure() method of your Startup.cs file, in the root location of Razor Pages, MVC and Blazor projects.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
    ...
    app.UseCookiePolicy();
    ...
 }

According to the official documentation, this “Adds the Microsoft.AspNetCore.CookiePolicy.CookiePolicyMiddleware handler to the specified Microsoft.AspNetCore.Builder.IApplicationBuilder, which enables cookie policy capabilities.
The cool thing about ASP .NET Core middleware is that there are many IApplicationBuilder extension methods for the necessary Middleware components you may need to use. Instead of hunting down each Middleware component, you can simply type app.Use in the Configure() method to discover what is available for you to use.

app.UseCookiePolicy() in Startup.cs
app.UseCookiePolicy() in Startup.cs

If you remove the call to app.UseCookiePolicy(), this will cause the aforementioned consentFeature value to be set to null in the C# code of your cookie popup.

var consentFeature = Context.Features.Get<ITrackingConsentFeature>(); 
cookieString is null if Cookie Policy disabled
cookieString is null if Cookie Policy disabled

There is also some minimal configuration that happens in the ConfigureServices() method which is called before the Configure() method in your Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
   services.Configure<CookiePolicyOptions>(options =>
   {
      // This lambda determines whether user consent for non-essential cookies is needed for a given request.
      options.CheckConsentNeeded = context => true;
      options.MinimumSameSitePolicy = SameSiteMode.None; 
   }); 
   ...
} 

The above code does a couple of things:

  1. As explained by the comment, the lambda (context => true) “determines whether user consent for non-essential cookies is needed for a given request” and then the CheckConsentNeeded boolean property for the options object is set to true or false.
  2. The property MinimumSameSitePolicy is set to SameSiteMode.None, which is an enumerator with the following possible values:
    • Unspecified = -1
    • None = 0
    • Lax = 1
    • Strict = 2

From the official documentation on cookie authentication,  The Cookie Policy Middleware setting for MinimumSameSitePolicy can affect the setting of Cookie.SameSite in CookieAuthenticationOptions settings. For more information, check out the documentation at:

References