From d221431825bb1c870c39990e91fb8ce6d52ed367 Mon Sep 17 00:00:00 2001 From: Roni Vegh Date: Wed, 8 Apr 2026 11:50:58 -0600 Subject: [PATCH] Implement health checks for Azure Key Vault CertificateClient and KeyClient --- .../AzureKeyVaultCertificatesComponent.cs | 5 +-- .../AzureKeyVaultCertificatesHealthCheck.cs | 32 +++++++++++++++++++ .../AzureKeyVaultKeysComponent.cs | 5 +-- .../AzureKeyVaultKeysHealthCheck.cs | 32 +++++++++++++++++++ .../CertificateClientConformanceTests.cs | 4 +-- .../KeyClientConformanceTests.cs | 4 +-- 6 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultCertificatesHealthCheck.cs create mode 100644 src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultKeysHealthCheck.cs diff --git a/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultCertificatesComponent.cs b/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultCertificatesComponent.cs index 33e85dc84c8..be38d477c9d 100644 --- a/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultCertificatesComponent.cs +++ b/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultCertificatesComponent.cs @@ -20,11 +20,8 @@ internal sealed class AzureKeyVaultCertificatesComponent : AbstractAzureKeyVault internal override CertificateClient CreateComponentClient(Uri vaultUri, CertificateClientOptions options, TokenCredential cred) => new(vaultUri, cred, options); - protected override bool GetHealthCheckEnabled(AzureSecurityKeyVaultSettings settings) - => false; - protected override IHealthCheck CreateHealthCheck(CertificateClient client, AzureSecurityKeyVaultSettings settings) - => throw new NotImplementedException(); + => new AzureKeyVaultCertificatesHealthCheck(client); protected override void BindClientOptionsToConfiguration(IAzureClientBuilder clientBuilder, IConfiguration configuration) #pragma warning disable IDE0200 // Remove unnecessary lambda expression - needed so the ConfigBinder Source Generator works diff --git a/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultCertificatesHealthCheck.cs b/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultCertificatesHealthCheck.cs new file mode 100644 index 00000000000..a04ffc8e270 --- /dev/null +++ b/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultCertificatesHealthCheck.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Azure.Security.KeyVault.Certificates; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Aspire.Azure.Security.KeyVault; + +internal sealed class AzureKeyVaultCertificatesHealthCheck : IHealthCheck +{ + private readonly CertificateClient _client; + + public AzureKeyVaultCertificatesHealthCheck(CertificateClient client) + => _client = client; + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + await foreach (var _ in _client.GetPropertiesOfCertificatesAsync(cancellationToken: cancellationToken).AsPages(pageSizeHint: 1).WithCancellation(cancellationToken).ConfigureAwait(false)) + { + break; + } + + return HealthCheckResult.Healthy(); + } + catch (Exception ex) + { + return new HealthCheckResult(context.Registration.FailureStatus, exception: ex); + } + } +} diff --git a/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultKeysComponent.cs b/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultKeysComponent.cs index 2688f890b11..b37199ff60e 100644 --- a/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultKeysComponent.cs +++ b/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultKeysComponent.cs @@ -13,11 +13,8 @@ namespace Microsoft.Extensions.Hosting; internal sealed class AzureKeyVaultKeysComponent : AbstractAzureKeyVaultComponent { - protected override bool GetHealthCheckEnabled(AzureSecurityKeyVaultSettings settings) - => false; - protected override IHealthCheck CreateHealthCheck(KeyClient client, AzureSecurityKeyVaultSettings settings) - => throw new NotImplementedException(); + => new AzureKeyVaultKeysHealthCheck(client); internal override KeyClient CreateComponentClient(Uri vaultUri, KeyClientOptions options, TokenCredential cred) => new(vaultUri, cred, options); diff --git a/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultKeysHealthCheck.cs b/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultKeysHealthCheck.cs new file mode 100644 index 00000000000..57d93ec176f --- /dev/null +++ b/src/Components/Aspire.Azure.Security.KeyVault/AzureKeyVaultKeysHealthCheck.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Azure.Security.KeyVault.Keys; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Aspire.Azure.Security.KeyVault; + +internal sealed class AzureKeyVaultKeysHealthCheck : IHealthCheck +{ + private readonly KeyClient _client; + + public AzureKeyVaultKeysHealthCheck(KeyClient client) + => _client = client; + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + await foreach (var _ in _client.GetPropertiesOfKeysAsync(cancellationToken).AsPages(pageSizeHint: 1).WithCancellation(cancellationToken).ConfigureAwait(false)) + { + break; + } + + return HealthCheckResult.Healthy(); + } + catch (Exception ex) + { + return new HealthCheckResult(context.Registration.FailureStatus, exception: ex); + } + } +} diff --git a/tests/Aspire.Azure.Security.KeyVault.Tests/CertificateClientConformanceTests.cs b/tests/Aspire.Azure.Security.KeyVault.Tests/CertificateClientConformanceTests.cs index eb9029e8255..f881921a15c 100644 --- a/tests/Aspire.Azure.Security.KeyVault.Tests/CertificateClientConformanceTests.cs +++ b/tests/Aspire.Azure.Security.KeyVault.Tests/CertificateClientConformanceTests.cs @@ -88,9 +88,7 @@ void ConfigureCredentials(AzureSecurityKeyVaultSettings settings) } protected override void SetHealthCheck(AzureSecurityKeyVaultSettings options, bool enabled) - // Disable Key Vault health check tests until https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/issues/2279 is fixed - // => options.DisableHealthChecks = !enabled; - => throw new NotImplementedException(); + => options.DisableHealthChecks = !enabled; protected override void SetMetrics(AzureSecurityKeyVaultSettings options, bool enabled) => throw new NotImplementedException(); diff --git a/tests/Aspire.Azure.Security.KeyVault.Tests/KeyClientConformanceTests.cs b/tests/Aspire.Azure.Security.KeyVault.Tests/KeyClientConformanceTests.cs index 141604c562b..1260409ef46 100644 --- a/tests/Aspire.Azure.Security.KeyVault.Tests/KeyClientConformanceTests.cs +++ b/tests/Aspire.Azure.Security.KeyVault.Tests/KeyClientConformanceTests.cs @@ -90,9 +90,7 @@ void ConfigureCredentials(AzureSecurityKeyVaultSettings settings) } protected override void SetHealthCheck(AzureSecurityKeyVaultSettings options, bool enabled) - // Disable Key Vault health check tests until https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/issues/2279 is fixed - // => options.DisableHealthChecks = !enabled; - => throw new NotImplementedException(); + => options.DisableHealthChecks = !enabled; protected override void SetMetrics(AzureSecurityKeyVaultSettings options, bool enabled) => throw new NotImplementedException();