Skip to content

Instantly share code, notes, and snippets.

@veleek
Last active October 7, 2019 15:16
Show Gist options
  • Save veleek/6751f70081e13d5f8bdf3f0cb09d0a4f to your computer and use it in GitHub Desktop.
Save veleek/6751f70081e13d5f8bdf3f0cb09d0a4f to your computer and use it in GitHub Desktop.
Minimal repro for KeyVault "No such host is known" error: https://github.com/Azure/azure-sdk-for-net/issues/7873
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.0.0-preview.4" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.0.0-preview.4" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Serilog" Version="2.8.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
</ItemGroup>
</Project>
using Azure;
using Azure.Security.KeyVault.Secrets;
using Serilog;
using Serilog.Context;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
namespace ContentKey.Cli
{
public static class Program
{
public static async Task Main(string[] args)
{
string logPath = Path.Combine(Environment.CurrentDirectory, $"ContentKeys.Cli_{DateTime.UtcNow:yyyy-MM-dd_hh-mm-ss}.log");
LoggerConfiguration loggerConfiguration = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.FromLogContext()
.WriteTo.Console(
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}]{Properties} {Message}{NewLine}{Exception}")
.WriteTo.File(
logPath,
outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fffTzzz} {Level:u3}]{Properties} {Message:lj}{NewLine}{Exception}");
Log.Logger = loggerConfiguration.CreateLogger();
var keySource = new KeyVaultContentKeySource(args[0]);
var keys = await keySource.LoadKeys();
Log.Information("Loaded {count} keys.", keys.Count);
}
}
public class KeyVaultContentKeySource
{
private readonly SecretClient secretClient;
public int MaximumParallelRequests { get; set; } = 25;
public KeyVaultContentKeySource(string vaultBaseUrl)
{
if (string.IsNullOrWhiteSpace(vaultBaseUrl))
{
throw new ArgumentNullException(nameof(vaultBaseUrl));
}
Log.Information("Connecting to KeyVault {keyVault}", vaultBaseUrl);
var creds = new Azure.Identity.InteractiveBrowserCredential("04b07795-8ddb-461a-bbee-02f9e1bf7b46");
secretClient = new SecretClient(new Uri($"https://{vaultBaseUrl}.vault.azure.net/"), creds);
}
public async IAsyncEnumerable<ContentKey> EnumerateKeysAsync()
{
int count = 0;
await foreach (Response<SecretBase> secret in secretClient.GetSecretsAsync())
{
yield return new ContentKey { Id = secret.Value.Name, Updated = secret.Value.Updated };
}
}
public async Task<ContentKey> LoadKeyAsync(string keyId)
{
Log.Debug("Loading key vault secret {secretId}", keyId);
Response<Secret> secret = await secretClient.GetAsync(keyId);
return new ContentKey
{
Id = secret.Value.Name,
Updated = secret.Value.Updated,
Value = secret.Value.Value
};
}
public async Task<List<ContentKey>> LoadKeys()
{
TransformBlock<ContentKey, ContentKey> loadKeyValue = new TransformBlock<ContentKey, ContentKey>(
async key =>
{
using IDisposable _ = LogContext.PushProperty("KeyId", key.Id);
Log.Information("Loading Key: {Updated}", key.Updated);
try
{
return await this.LoadKeyAsync(key.Id);
}
catch (Exception e)
{
Log.Error(e, "Failed to load key");
return null;
}
},
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = MaximumParallelRequests == 0 ? 1 : MaximumParallelRequests }
);
List<ContentKey> contentKeys = new List<ContentKey>();
ActionBlock<ContentKey> addToList = new ActionBlock<ContentKey>(key =>
{
if (key != null)
{
contentKeys.Add(key);
}
});
loadKeyValue.LinkTo(addToList, new DataflowLinkOptions { PropagateCompletion = true });
await foreach (var contentKey in this.EnumerateKeysAsync())
{
loadKeyValue.Post(contentKey);
}
loadKeyValue.Complete();
await loadKeyValue.Completion;
Log.Information("Updated list contains {count} keys", contentKeys.Count);
return contentKeys;
}
}
public class ContentKey
{
public string Id { get; set; }
public DateTimeOffset? Updated { get; set; }
public string Value { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment