Skip to content
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
16 changes: 16 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
trigger:
- main
- preview
- release/*

pr:
- main
- preview
- release/*

pool:
vmImage: ubuntu-latest

steps:
- script: echo "Hello from Azure Pipelines"
displayName: Hello world
8 changes: 8 additions & 0 deletions examples/ConfigStoreDemo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ public static IWebHost BuildWebHost(string[] args)
// 3. Set up the provider to listen for changes to the background color key-value in Azure App Configuration

var settings = config.AddJsonFile("appsettings.json").Build();

if (string.IsNullOrEmpty(settings["connection_string"]))
{
throw new InvalidOperationException(
"Connection string not found. " +
"Please set the 'connection_string' in appsettings.json.");
}

config.AddAzureAppConfiguration(options =>
{
options.Connect(settings["connection_string"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.15.0" />
<PackageReference Include="Azure.Identity" Version="1.21.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
</ItemGroup>
Expand Down
5 changes: 4 additions & 1 deletion examples/ConsoleAppWithFailOver/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ private static void Configure()
IConfiguration configuration = builder.Build();

IConfigurationSection endpointsSection = configuration.GetSection("AppConfig:Endpoints");
IEnumerable<Uri> endpoints = endpointsSection.GetChildren().Select(endpoint => new Uri(endpoint.Value));
IEnumerable<Uri> endpoints = endpointsSection.GetChildren()
.Select(endpoint => endpoint.Value)
.Where(value => !string.IsNullOrEmpty(value))
.Select(value => new Uri(value));

if (endpoints == null || !endpoints.Any())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<!-- Nuget Package Version Settings -->

<PropertyGroup>
<OfficialVersion>8.5.0</OfficialVersion>
<OfficialVersion>8.6.0</OfficialVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(CDP_PATCH_NUMBER)'!='' AND '$(CDP_BUILD_TYPE)'=='Official'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<!-- Nuget Package Version Settings -->

<PropertyGroup>
<OfficialVersion>8.5.0</OfficialVersion>
<OfficialVersion>8.6.0</OfficialVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(CDP_PATCH_NUMBER)'!='' AND '$(CDP_BUILD_TYPE)'=='Official'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public class AzureAppConfigurationKeyVaultOptions
internal TimeSpan? DefaultSecretRefreshInterval = null;
internal bool IsKeyVaultRefreshConfigured = false;

/// <summary>
/// Flag to indicate whether Key Vault references should be resolved in parallel. Disabled by default.
/// </summary>
public bool ParallelSecretResolutionEnabled { get; set; }

/// <summary>
/// Sets the credentials used to authenticate to key vaults that have no registered <see cref="SecretClient"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,12 @@ internal IEnumerable<IKeyValueAdapter> Adapters
/// <summary>
/// Flag to indicate whether Key Vault options have been configured.
/// </summary>
internal bool IsKeyVaultConfigured { get; private set; } = false;
internal bool IsKeyVaultConfigured { get; private set; }

/// <summary>
/// Flag to indicate whether Key Vault secret values will be refreshed automatically.
/// </summary>
internal bool IsKeyVaultRefreshConfigured { get; private set; } = false;
internal bool IsKeyVaultRefreshConfigured { get; private set; }

/// <summary>
/// Indicates all feature flag features used by the application.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,16 +625,19 @@ private async Task<Dictionary<string, string>> PrepareData(Dictionary<string, Co
_requestTracingOptions.ResetAiConfigurationTracing();
}

foreach (KeyValuePair<string, ConfigurationSetting> kvp in data)
foreach (IKeyValueAdapter adapter in _options.Adapters)
{
IEnumerable<KeyValuePair<string, string>> keyValuePairs = null;
await adapter.PreloadAsync(data.Values, _logger, cancellationToken).ConfigureAwait(false);
}

foreach (KeyValuePair<string, ConfigurationSetting> kvp in data)
{
if (_requestTracingEnabled && _requestTracingOptions != null)
{
_requestTracingOptions.UpdateAiConfigurationTracing(kvp.Value.ContentType);
}

keyValuePairs = await ProcessAdapters(kvp.Value, cancellationToken).ConfigureAwait(false);
IEnumerable<KeyValuePair<string, string>> keyValuePairs = await ProcessAdapters(kvp.Value, cancellationToken).ConfigureAwait(false);

foreach (KeyValuePair<string, string> kv in keyValuePairs)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Azure;
using Azure.Data.AppConfiguration;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mime;
using System.Text.Json;
using System.Threading;
Expand All @@ -18,8 +16,6 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.AzureKeyVault
{
internal class AzureKeyVaultKeyValueAdapter : IKeyValueAdapter
{
private const string AzureIdentityAssemblyName = "Azure.Identity";

private readonly AzureKeyVaultSecretProvider _secretProvider;

public AzureKeyVaultKeyValueAdapter(AzureKeyVaultSecretProvider secretProvider)
Expand All @@ -37,42 +33,17 @@ public async Task<IEnumerable<KeyValuePair<string, string>>> ProcessKeyValue(Con
// Uri validation
if (string.IsNullOrEmpty(secretRefUri) || !Uri.TryCreate(secretRefUri, UriKind.Absolute, out Uri secretUri) || !KeyVaultSecretIdentifier.TryCreate(secretUri, out KeyVaultSecretIdentifier secretIdentifier))
{
throw CreateKeyVaultReferenceException("Invalid Key vault secret identifier.", setting, null, secretRefUri);
throw KeyVaultReferenceException.Create("Invalid Key vault secret identifier.", setting, null, secretRefUri);
}

string secret;

try
{
secret = await _secretProvider.GetSecretValue(secretIdentifier, setting.Key, setting.Label, logger, cancellationToken).ConfigureAwait(false);
}
catch (Exception e) when (e is UnauthorizedAccessException || (e.Source?.Equals(AzureIdentityAssemblyName, StringComparison.OrdinalIgnoreCase) ?? false))
{
throw CreateKeyVaultReferenceException(e.Message, setting, e, secretRefUri);
}
catch (Exception e) when (e is RequestFailedException || ((e as AggregateException)?.InnerExceptions?.All(e => e is RequestFailedException) ?? false))
{
throw CreateKeyVaultReferenceException("Key vault error.", setting, e, secretRefUri);
}
string secret = await _secretProvider.GetSecretValue(secretIdentifier, setting, logger, cancellationToken).ConfigureAwait(false);

return new KeyValuePair<string, string>[]
{
new KeyValuePair<string, string>(setting.Key, secret)
};
}

KeyVaultReferenceException CreateKeyVaultReferenceException(string message, ConfigurationSetting setting, Exception inner, string secretRefUri = null)
{
return new KeyVaultReferenceException(message, inner)
{
Key = setting.Key,
Label = setting.Label,
Etag = setting.ETag.ToString(),
ErrorCode = (inner as RequestFailedException)?.ErrorCode,
SecretIdentifier = secretRefUri
};
}

public bool CanProcess(ConfigurationSetting setting)
{
if (setting == null ||
Expand Down Expand Up @@ -116,6 +87,81 @@ public bool NeedsRefresh()
return _secretProvider.ShouldRefreshKeyVaultSecrets();
}

public async Task PreloadAsync(IEnumerable<ConfigurationSetting> settings, Logger logger, CancellationToken cancellationToken)
{
if (settings == null)
{
return;
}

var seen = new HashSet<Uri>();
var toFetch = new List<(KeyVaultSecretIdentifier, ConfigurationSetting)>();

foreach (ConfigurationSetting setting in settings)
{
if (!CanProcess(setting))
{
continue;
}

string secretRefUri = ParseSecretReferenceUri(setting);

if (string.IsNullOrEmpty(secretRefUri) ||
!Uri.TryCreate(secretRefUri, UriKind.Absolute, out Uri secretUri) ||
!KeyVaultSecretIdentifier.TryCreate(secretUri, out KeyVaultSecretIdentifier secretIdentifier))
{
throw KeyVaultReferenceException.Create("Invalid Key vault secret identifier.", setting, null, secretRefUri);
}

if (seen.Add(secretIdentifier.SourceId))
{
toFetch.Add((secretIdentifier, setting));
}
}

if (toFetch.Count == 0)
{
return;
}

if (_secretProvider.IsParallelSecretResolutionEnabled)
{
using (var semaphore = new SemaphoreSlim(KeyVaultConstants.MaxParallelSecretResolution))
{
async Task ResolveSecretAsync(KeyVaultSecretIdentifier identifier, ConfigurationSetting setting)
{
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);

try
{
await _secretProvider.GetSecretValue(identifier, setting, logger, cancellationToken).ConfigureAwait(false);
}
finally
{
semaphore.Release();
}
}

Task[] tasks = new Task[toFetch.Count];

for (int i = 0; i < toFetch.Count; i++)
{
(KeyVaultSecretIdentifier identifier, ConfigurationSetting setting) = toFetch[i];
tasks[i] = ResolveSecretAsync(identifier, setting);
}

await Task.WhenAll(tasks).ConfigureAwait(false);
}
}
else
{
foreach ((KeyVaultSecretIdentifier identifier, ConfigurationSetting setting) in toFetch)
{
await _secretProvider.GetSecretValue(identifier, setting, logger, cancellationToken).ConfigureAwait(false);
}
}
}

private string ParseSecretReferenceUri(ConfigurationSetting setting)
{
string secretRefUri = null;
Expand All @@ -126,7 +172,7 @@ private string ParseSecretReferenceUri(ConfigurationSetting setting)

if (reader.Read() && reader.TokenType != JsonTokenType.StartObject)
{
throw CreateKeyVaultReferenceException(ErrorMessages.InvalidKeyVaultReference, setting, null, null);
throw KeyVaultReferenceException.Create(ErrorMessages.InvalidKeyVaultReference, setting, null, null);
}

while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
Expand All @@ -144,7 +190,7 @@ private string ParseSecretReferenceUri(ConfigurationSetting setting)
}
else
{
throw CreateKeyVaultReferenceException(ErrorMessages.InvalidKeyVaultReference, setting, null, null);
throw KeyVaultReferenceException.Create(ErrorMessages.InvalidKeyVaultReference, setting, null, null);
}
}
else
Expand All @@ -155,7 +201,7 @@ private string ParseSecretReferenceUri(ConfigurationSetting setting)
}
catch (JsonException e)
{
throw CreateKeyVaultReferenceException(ErrorMessages.InvalidKeyVaultReference, setting, e, null);
throw KeyVaultReferenceException.Create(ErrorMessages.InvalidKeyVaultReference, setting, e, null);
}

return secretRefUri;
Expand Down
Loading
Loading