sovereignApplications/confidential/contosoHR/src/Startup.cs (111 lines of code) (raw):

// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Azure.Core; using Azure.Identity; using ContosoHR.Models; using ContosoHR.Util; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Text; using System.Text.Json.Nodes; using System.Threading.Tasks; /// <summary> /// Startup app configurations. /// </summary> namespace ContosoHR { public class Startup { public Startup(IConfiguration configuration) { IsDevelopment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"; if (IsDevelopment) { // For dev-side debugging, ask dev to log in, and get config from appConfig file ClientCredential = new InteractiveBrowserCredential(); ConnectionString = configuration.GetConnectionString("ContosoHRDatabase"); ConfidentialLedgerName = configuration.GetValue<string>("ConfidentialLedgerName"); } else { // Production credential picked up from VM environment and config from IMDS ClientCredential = new DefaultAzureCredential(); string imdsUserObject = GetUserObjectFromImdsAsync().Result; JsonNode configurationFromImds = JsonNode.Parse(imdsUserObject)!; ConnectionString = (string)configurationFromImds["ContosoHRDatabase"]; ConfidentialLedgerName = (string)configurationFromImds["ConfidentialLedgerName"]; } InitializeAzureKeyVaultProvider(); } private bool IsDevelopment { get; set; } private TokenCredential ClientCredential { get; set; } private string ConnectionString { get; set; } private string ConfidentialLedgerName { get; set; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddSingleton(new ConfidentialLedgerLogger(ConfidentialLedgerName, "ContosoHrSqlLogs", ClientCredential)); services.AddDbContext<ContosoHRContext>(options => options.UseSqlServer(ConnectionString)); services.AddControllers(); services.AddRazorPages(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapRazorPages(); }); } private static async Task<string> GetUserObjectFromImdsAsync() { string connectionString = string.Empty; using (var httpClient = new HttpClient()) { // IMDS requires bypassing proxies. WebProxy proxy = new WebProxy(); HttpClient.DefaultProxy = proxy; httpClient.DefaultRequestHeaders.Add("Metadata", "True"); try { var b64connString = await httpClient.GetStringAsync("http://169.254.169.254/metadata/instance/compute/userData?api-version=2021-01-01&format=text"); var b64Bytes = Convert.FromBase64String(b64connString); return Encoding.UTF8.GetString(b64Bytes); } catch (AggregateException ex) { // Handle response failures Console.WriteLine("Request failed: " + ex.GetBaseException()); } } return connectionString; } private void InitializeAzureKeyVaultProvider() { // Initialize the Azure Key Vault provider SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(ClientCredential); // Register the Azure Key Vault provider SqlConnection.RegisterColumnEncryptionKeyStoreProviders( customProviders: new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>( capacity: 1, comparer: StringComparer.OrdinalIgnoreCase) { { SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, sqlColumnEncryptionAzureKeyVaultProvider } } ); } } }