Skip to content

Instantly share code, notes, and snippets.

@justinyoo
Last active February 16, 2021 22:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justinyoo/75c16e773d9e1c8b8a1d5d5efa37f9c9 to your computer and use it in GitHub Desktop.
Save justinyoo/75c16e773d9e1c8b8a1d5d5efa37f9c9 to your computer and use it in GitHub Desktop.
KeyVault Secrets Rotation Management
@Microsoft.KeyVault(SecretUri=https://<keyvault_name>.vault.azure.net/secrets/<secret_name>)
dotnet add package Azure.Security.KeyVault.Secrets --version 4.2.0-beta.4
dotnet add package Azure.Identity --version 1.4.0-beta.3
dotnet add package System.Linq.Async --version 4.1.1
func new --name BulkDisableSecretsHttpTrigger --template HttpTrigger --language C#
public static class BulkDisableSecretsHttpTrigger
{
[FunctionName("BulkDisableSecretsHttpTrigger")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "POST", Route = "secrets/all/disable")] HttpRequest req,
ILogger log)
{
// Get the KeyVault URI
var uri = Environment.GetEnvironmentVariable("KeyVault__Uri");
// Get the tenant ID where the KeyVault lives
var tenantId = Environment.GetEnvironmentVariable("KeyVault__TenantId");
// Set the tenant ID, in case your account has multiple tenants logged in
var options = new DefaultAzureCredentialOptions()
{
SharedTokenCacheTenantId = tenantId,
VisualStudioTenantId = tenantId,
VisualStudioCodeTenantId = tenantId,
};
var client = new SecretClient(new Uri(uri), new DefaultAzureCredential(options));
// Get the all secrets
var secrets = await client.GetPropertiesOfSecretsAsync()
.ToListAsync()
.ConfigureAwait(false);
var utcNow = DateTimeOffset.UtcNow;
var results = new Dictionary<string, object>();
foreach (var secret in secrets)
{
// Get the all versions of the given secret
// Filter only enabled versions
// Sort by the created date in a reverse order
var versions = await client.GetPropertiesOfSecretVersionsAsync(secret.Name)
.WhereAwait(p => new ValueTask<bool>(p.Enabled.GetValueOrDefault() == true))
.OrderByDescendingAwait(p => new ValueTask<DateTimeOffset>(p.CreatedOn.GetValueOrDefault()))
.ToListAsync()
.ConfigureAwait(false);
// Do nothing if there is no version enabled
if (!versions.Any())
{
continue;
}
// Do nothing if there is only one version enabled
if (versions.Count < 2)
{
continue;
}
// Do nothing if the latest version was generated less than a day ago
if (versions.First().CreatedOn.GetValueOrDefault() <= utcNow.AddDays(-1))
{
continue;
}
// Disable all versions except the first (latest) one
var candidates = versions.Skip(1).ToList();
var result = new List<SecretProperties>() { versions.First() };
foreach (var candidate in candidates)
{
candidate.Enabled = false;
var response = await client.UpdateSecretPropertiesAsync(candidate).ConfigureAwait(false);
result.Add(response.Value);
}
results.Add(secret.Name, result);
}
var res = new ContentResult()
{
Content = JsonConvert.SerializeObject(results, Formatting.Indented),
ContentType = "application/json",
};
return res;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment