Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App insights key lookup occurs only when configuration option is set #69

Merged
merged 3 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions src/TesApi.Web/Management/ArmResourceInformationFinder.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Provides utility methods to find resource information using the TES service identity
/// </summary>
public class ArmResourceInformationFinder
{
/// <summary>
/// Looks up the AppInsights instrumentation key in subscriptions the TES services has access to
/// </summary>
/// <param name="accountName"></param>
/// <returns></returns>
public static async Task<string> 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<string> GetAzureAccessTokenAsync(string resource = "https://management.azure.com/")
{
return new AzureServiceTokenProvider().GetAccessTokenAsync(resource);
}

/// <summary>
/// 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.
/// </summary>
/// <param name="batchAccountName">batch account name</param>
/// <returns></returns>
public static async Task<BatchAccountResourceInformation> 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;
}
}
}
31 changes: 0 additions & 31 deletions src/TesApi.Web/Management/AzureManagementClientsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -71,36 +70,6 @@ public async Task<BatchManagementClient> CreateBatchAccountManagementClient()
public async Task<FluentAzure.IAuthenticated> CreateAzureManagementClientAsync()
=> await AzureManagementClientsFactory.GetAzureManagementClientAsync();

/// <summary>
/// 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.
/// </summary>
/// <param name="batchAccountName">batch account name</param>
/// <returns></returns>
public static async Task<BatchAccountResourceInformation> 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;
}

/// <summary>
/// Creates a new instance of Azure Management client
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class BatchAccountOptions
/// <summary>
/// Configuration section.
/// </summary>
public const string BatchAccount = "BatchAccount";
public const string SectionName = "BatchAccount";
/// <summary>
/// Default Azure offer durable id.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class ContainerRegistryOptions
/// <summary>
/// Configuration section.
/// </summary>
public const string ContainerRegistrySection = "ContainerRegistry";
public const string SectionName = "ContainerRegistry";
/// <summary>
/// Enables/disables auto-discovery features
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class RetryPolicyOptions
/// <summary>
/// Retry policy configuration section
/// </summary>
public const string RetryPolicy = "RetryPolicy";
public const string SectionName = "RetryPolicy";

private const int DefaultRetryCount = 3;
private const int DefaultExponentialBackOffExponent = 2;
Expand Down
2 changes: 1 addition & 1 deletion src/TesApi.Web/Management/Configuration/TerraOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class TerraOptions
/// <summary>
/// Terra configuration section
/// </summary>
public const string Terra = "Terra";
public const string SectionName = "Terra";
private const int DefaultSasTokenExpirationInSeconds = 60 * 24 * 3; // 3 days

/// <summary>
Expand Down
68 changes: 35 additions & 33 deletions src/TesApi.Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,19 @@ public Startup(IConfiguration configuration, ILogger<Startup> logger, IWebHostEn
/// </summary>
/// <param name="services">The Microsoft.Extensions.DependencyInjection.IServiceCollection to add the services to.</param>
public void ConfigureServices(IServiceCollection services)
=> services
.Configure<BatchAccountOptions>(Configuration.GetSection(BatchAccountOptions.BatchAccount))
{
services
.Configure<BatchAccountOptions>(Configuration.GetSection(BatchAccountOptions.SectionName))
.Configure<CosmosDbOptions>(Configuration.GetSection(CosmosDbOptions.CosmosDbAccount))
.Configure<RetryPolicyOptions>(Configuration.GetSection(RetryPolicyOptions.RetryPolicy))
.Configure<TerraOptions>(Configuration.GetSection(TerraOptions.Terra))
.Configure<ContainerRegistryOptions>(Configuration.GetSection(ContainerRegistryOptions.ContainerRegistrySection))
.Configure<RetryPolicyOptions>(Configuration.GetSection(RetryPolicyOptions.SectionName))
.Configure<TerraOptions>(Configuration.GetSection(TerraOptions.SectionName))
.Configure<ContainerRegistryOptions>(Configuration.GetSection(ContainerRegistryOptions.SectionName))
.AddSingleton<IAppCache, CachingService>()
.AddSingleton(CreateBatchPoolManagerFromConfiguration)

.AddSingleton<AzureProxy, AzureProxy>()

.AddSingleton<AzureProxy>()
.AddSingleton(CreateCosmosDbRepositoryFromConfiguration)
.AddSingleton<IBatchPoolFactory, BatchPoolFactory>()
.AddTransient<BatchPool>()
.AddSingleton(CreateBatchPoolManagerFromConfiguration)

.AddControllers()
.AddNewtonsoftJson(opts =>
Expand All @@ -97,9 +96,8 @@ public void ConfigureServices(IServiceCollection services)
.AddSingleton<IBatchSkuInformationProvider, PriceApiBatchSkuInformationProvider>()
.AddSingleton(CreateBatchAccountResourceInformation)
.AddSingleton(CreateBatchQuotaProviderFromConfiguration)
.AddSingleton<AzureManagementClientsFactory, AzureManagementClientsFactory>()
//.AddSingleton<ArmBatchQuotaProvider, ArmBatchQuotaProvider>() //added so config utils gets the arm implementation, to be removed once config utils is refactored.
.AddSingleton<ConfigurationUtils, ConfigurationUtils>()
.AddSingleton<AzureManagementClientsFactory>()
.AddSingleton<ConfigurationUtils>()
.AddSingleton<TokenCredential>(s => new DefaultAzureCredential())

.AddSwaggerGen(c =>
Expand Down Expand Up @@ -132,29 +130,33 @@ public void ConfigureServices(IServiceCollection services)
.AddHostedService<Scheduler>()
.AddHostedService<DeleteCompletedBatchJobsHostedService>()
.AddHostedService<DeleteOrphanedBatchJobsHostedService>()
.AddHostedService<DeleteOrphanedAutoPoolsHostedService>()
//.AddHostedService<RefreshVMSizesAndPricesHostedService>()
.AddHostedService<DeleteOrphanedAutoPoolsHostedService>();
//.AddHostedService<RefreshVMSizesAndPricesHostedService>()


//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)
{
Expand Down Expand Up @@ -249,7 +251,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)
Expand Down