From 592a597c0490610438f7f3e66fbff17a474b7183 Mon Sep 17 00:00:00 2001 From: giventocode <3589801+giventocode@users.noreply.github.com> Date: Mon, 6 Feb 2023 13:26:22 -0500 Subject: [PATCH] lookup does not occur in production, refactoring, --- .../ArmResourceInformationFinder.cs | 77 +++++++++++++++++++ .../AzureManagementClientsFactory.cs | 31 -------- .../Configuration/BatchAccountOptions.cs | 2 +- .../Configuration/ContainerRegistryOptions.cs | 2 +- .../Configuration/RetryPolicyOptions.cs | 2 +- .../Management/Configuration/TerraOptions.cs | 2 +- src/TesApi.Web/Startup.cs | 61 ++++++++------- 7 files changed, 114 insertions(+), 63 deletions(-) create mode 100644 src/TesApi.Web/Management/ArmResourceInformationFinder.cs diff --git a/src/TesApi.Web/Management/ArmResourceInformationFinder.cs b/src/TesApi.Web/Management/ArmResourceInformationFinder.cs new file mode 100644 index 000000000..740c2eb0a --- /dev/null +++ b/src/TesApi.Web/Management/ArmResourceInformationFinder.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Azure.Management.ApplicationInsights.Management; +using Microsoft.Azure.Management.Batch; +using Microsoft.Azure.Services.AppAuthentication; +using Microsoft.Rest; + +namespace TesApi.Web.Management +{ + /// + /// Provides utility methods to find resource information using the TES service identity + /// + public class ArmResourceInformationFinder + { + /// + /// Looks up the AppInsights instrumentation key in subscriptions the TES services has access to + /// + /// + /// + public static async Task GetAppInsightsInstrumentationKeyAsync(string accountName) + { + var azureClient = await AzureManagementClientsFactory.GetAzureManagementClientAsync(); + var subscriptionIds = (await azureClient.Subscriptions.ListAsync()).Select(s => s.SubscriptionId); + + var credentials = new TokenCredentials(await GetAzureAccessTokenAsync()); + + foreach (var subscriptionId in subscriptionIds) + { + var app = (await new ApplicationInsightsManagementClient(credentials) { SubscriptionId = subscriptionId }.Components.ListAsync()) + .FirstOrDefault(a => a.ApplicationId.Equals(accountName, StringComparison.OrdinalIgnoreCase)); + + if (app is not null) + { + return app.InstrumentationKey; + } + } + + return null; + } + //TODO: refactor this to use Azure Identity token provider. + private static Task GetAzureAccessTokenAsync(string resource = "https://management.azure.com/") + { + return new AzureServiceTokenProvider().GetAccessTokenAsync(resource); + } + + /// + /// Attempts to get the batch resource information using the ARM api. + /// Returns null if the resource was not found or the account does not have access. + /// + /// batch account name + /// + public static async Task TryGetResourceInformationFromAccountNameAsync(string batchAccountName) + { + //TODO: look if a newer version of the management SDK provides a simpler way to look for this information . + var tokenCredentials = new TokenCredentials(await GetAzureAccessTokenAsync()); + var azureClient = await AzureManagementClientsFactory.GetAzureManagementClientAsync(); + + var subscriptionIds = (await azureClient.Subscriptions.ListAsync()).Select(s => s.SubscriptionId); + + foreach (var subId in subscriptionIds) + { + using var batchClient = new BatchManagementClient(tokenCredentials) { SubscriptionId = subId }; + + var batchAccount = (await batchClient.BatchAccount.ListAsync()) + .FirstOrDefault(a => a.Name.Equals(batchAccountName, StringComparison.OrdinalIgnoreCase)); + + if (batchAccount is not null) + { + return BatchAccountResourceInformation.FromBatchResourceId(batchAccount.Id, batchAccount.Location); + } + } + + return null; + } + } +} diff --git a/src/TesApi.Web/Management/AzureManagementClientsFactory.cs b/src/TesApi.Web/Management/AzureManagementClientsFactory.cs index f64a43bc4..4a98582cb 100644 --- a/src/TesApi.Web/Management/AzureManagementClientsFactory.cs +++ b/src/TesApi.Web/Management/AzureManagementClientsFactory.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Linq; using System.Threading.Tasks; using Microsoft.Azure.Management.Batch; using Microsoft.Azure.Management.ResourceManager.Fluent; @@ -71,36 +70,6 @@ public async Task CreateBatchAccountManagementClient() public async Task CreateAzureManagementClientAsync() => await AzureManagementClientsFactory.GetAzureManagementClientAsync(); - /// - /// Attempts to get the batch resource information using the ARM api. - /// Returns null if the resource was not found or the account does not have access. - /// - /// batch account name - /// - public static async Task TryGetResourceInformationFromAccountNameAsync(string batchAccountName) - { - //TODO: look if a newer version of the management SDK provides a simpler way to look for this information . - var tokenCredentials = new TokenCredentials(await GetAzureAccessTokenAsync()); - var azureClient = await GetAzureManagementClientAsync(); - - var subscriptionIds = (await azureClient.Subscriptions.ListAsync()).Select(s => s.SubscriptionId); - - foreach (var subId in subscriptionIds) - { - using var batchClient = new BatchManagementClient(tokenCredentials) { SubscriptionId = subId }; - - var batchAccount = (await batchClient.BatchAccount.ListAsync()) - .FirstOrDefault(a => a.Name.Equals(batchAccountName, StringComparison.OrdinalIgnoreCase)); - - if (batchAccount is not null) - { - return BatchAccountResourceInformation.FromBatchResourceId(batchAccount.Id, batchAccount.Location); - } - } - - return null; - } - /// /// Creates a new instance of Azure Management client /// diff --git a/src/TesApi.Web/Management/Configuration/BatchAccountOptions.cs b/src/TesApi.Web/Management/Configuration/BatchAccountOptions.cs index 10eb04178..5350f8ab3 100644 --- a/src/TesApi.Web/Management/Configuration/BatchAccountOptions.cs +++ b/src/TesApi.Web/Management/Configuration/BatchAccountOptions.cs @@ -11,7 +11,7 @@ public class BatchAccountOptions /// /// Configuration section. /// - public const string BatchAccount = "BatchAccount"; + public const string SectionName = "BatchAccount"; /// /// Default Azure offer durable id. /// diff --git a/src/TesApi.Web/Management/Configuration/ContainerRegistryOptions.cs b/src/TesApi.Web/Management/Configuration/ContainerRegistryOptions.cs index e51d0a7e5..feb997ba0 100644 --- a/src/TesApi.Web/Management/Configuration/ContainerRegistryOptions.cs +++ b/src/TesApi.Web/Management/Configuration/ContainerRegistryOptions.cs @@ -11,7 +11,7 @@ public class ContainerRegistryOptions /// /// Configuration section. /// - public const string ContainerRegistrySection = "ContainerRegistry"; + public const string SectionName = "ContainerRegistry"; /// /// Enables/disables auto-discovery features /// diff --git a/src/TesApi.Web/Management/Configuration/RetryPolicyOptions.cs b/src/TesApi.Web/Management/Configuration/RetryPolicyOptions.cs index 2fdfc3a89..5774fa6b8 100644 --- a/src/TesApi.Web/Management/Configuration/RetryPolicyOptions.cs +++ b/src/TesApi.Web/Management/Configuration/RetryPolicyOptions.cs @@ -11,7 +11,7 @@ public class RetryPolicyOptions /// /// Retry policy configuration section /// - public const string RetryPolicy = "RetryPolicy"; + public const string SectionName = "RetryPolicy"; private const int DefaultRetryCount = 3; private const int DefaultExponentialBackOffExponent = 2; diff --git a/src/TesApi.Web/Management/Configuration/TerraOptions.cs b/src/TesApi.Web/Management/Configuration/TerraOptions.cs index 07d0b9d59..f209b8d77 100644 --- a/src/TesApi.Web/Management/Configuration/TerraOptions.cs +++ b/src/TesApi.Web/Management/Configuration/TerraOptions.cs @@ -11,7 +11,7 @@ public class TerraOptions /// /// Terra configuration section /// - public const string Terra = "Terra"; + public const string SectionName = "Terra"; private const int DefaultSasTokenExpirationInSeconds = 60 * 24 * 3; // 3 days /// diff --git a/src/TesApi.Web/Startup.cs b/src/TesApi.Web/Startup.cs index 654900629..8d73d1596 100644 --- a/src/TesApi.Web/Startup.cs +++ b/src/TesApi.Web/Startup.cs @@ -59,16 +59,18 @@ public Startup(IConfiguration configuration, ILogger logger, IWebHostEn /// /// The Microsoft.Extensions.DependencyInjection.IServiceCollection to add the services to. public void ConfigureServices(IServiceCollection services) - => services - .Configure(Configuration.GetSection(BatchAccountOptions.BatchAccount)) + { + services + .Configure(Configuration.GetSection(BatchAccountOptions.SectionName)) .Configure(Configuration.GetSection(CosmosDbOptions.CosmosDbAccount)) - .Configure(Configuration.GetSection(RetryPolicyOptions.RetryPolicy)) - .Configure(Configuration.GetSection(TerraOptions.Terra)) + .Configure(Configuration.GetSection(RetryPolicyOptions.SectionName)) + .Configure(Configuration.GetSection(TerraOptions.SectionName)) .AddSingleton() .AddSingleton() - .AddSingleton(sp => ActivatorUtilities.CreateInstance(sp, (IAzureProxy)sp.GetRequiredService(typeof(AzureProxy)))) - + .AddSingleton(sp => + ActivatorUtilities.CreateInstance(sp, + (IAzureProxy)sp.GetRequiredService(typeof(AzureProxy)))) .AddSingleton(CreateCosmosDbRepositoryFromConfiguration) .AddSingleton() .AddTransient() @@ -93,7 +95,6 @@ public void ConfigureServices(IServiceCollection services) .AddSingleton(CreateBatchAccountResourceInformation) .AddSingleton(CreateBatchQuotaProviderFromConfiguration) .AddSingleton() - .AddSingleton() //added so config utils gets the arm implementation, to be removed once config utils is refactored. .AddSingleton() .AddSingleton(s => new DefaultAzureCredential()) @@ -127,29 +128,33 @@ public void ConfigureServices(IServiceCollection services) .AddHostedService() .AddHostedService() .AddHostedService() - .AddHostedService() - //.AddHostedService() + .AddHostedService(); + //.AddHostedService() - //Configure AppInsights Azure Service when in PRODUCTION environment - .IfThenElse(hostingEnvironment.IsProduction(), - s => - { - var applicationInsightsAccountName = Configuration["ApplicationInsightsAccountName"]; - var instrumentationKey = AzureProxy.GetAppInsightsInstrumentationKeyAsync(applicationInsightsAccountName).Result; + AddAppInsightsWithDefaultOrLookingUpTheConnectionString(services); + } - if (instrumentationKey is not null) - { - var connectionString = $"InstrumentationKey={instrumentationKey}"; - return s.AddApplicationInsightsTelemetry(options => - { - options.ConnectionString = connectionString; - }); - } - - return s; - }, - s => s.AddApplicationInsightsTelemetry()); + private void AddAppInsightsWithDefaultOrLookingUpTheConnectionString(IServiceCollection services) + { + var accountName = Configuration["ApplicationInsightsAccountName"]; + + if (string.IsNullOrEmpty(accountName)) + { + //use default settings that will use the app insights configuration + services.AddApplicationInsightsTelemetry(); + return; + } + + services.AddApplicationInsightsTelemetry(s => + { + var instrumentationKey = ArmResourceInformationFinder + .GetAppInsightsInstrumentationKeyAsync(accountName) + .Result; + + s.ConnectionString = $"InstrumentationKey={instrumentationKey}"; + }); + } private IBatchQuotaProvider CreateBatchQuotaProviderFromConfiguration(IServiceProvider services) { @@ -227,7 +232,7 @@ private BatchAccountResourceInformation CreateBatchAccountResourceInformation(IS if (string.IsNullOrWhiteSpace(options.Value.AppKey)) { //we are assuming Arm with MI/RBAC if no key is provided. Try to get info from the batch account. - var task = AzureManagementClientsFactory.TryGetResourceInformationFromAccountNameAsync(options.Value.AccountName); + var task = ArmResourceInformationFinder.TryGetResourceInformationFromAccountNameAsync(options.Value.AccountName); task.Wait(); if (task.Result == null)