Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
433ab73
save changes to keyvaultclient
scbedd Sep 17, 2025
866c9ee
with that challenge disable, we are able to pass a keyvault list test!
scbedd Sep 18, 2025
f10c62b
KeyVaultService restored to main
scbedd Sep 25, 2025
debc106
saving original version premerge
scbedd Sep 25, 2025
d96c71c
add a tsp-location.yaml
scbedd Sep 25, 2025
d2f2a99
need to update emitter output location. but it's working!
scbedd Sep 25, 2025
defb2d3
Merge branch 'main' into support-record-playback-via-url-rewrite
scbedd Oct 1, 2025
bbbf0e2
configure test-proxy to download for the package
scbedd Oct 3, 2025
e6860dd
adding generated test proxy client (with some manual modifications fo…
scbedd Oct 7, 2025
b2f51cb
update emitter-package.json. updates to reflect new TestProxy stuff. …
scbedd Oct 8, 2025
9e6555d
save changes. still need to refine quite a bit
scbedd Oct 8, 2025
6cc2a9c
simplify TestProxy.cs
scbedd Oct 8, 2025
0f191a6
merge main
scbedd Oct 14, 2025
04b75ee
heading towards a class fixture approach
scbedd Oct 14, 2025
c56c4a1
add proxy class fixture to CommandTestsBase
scbedd Oct 14, 2025
551717c
a class fixture will suck unfortunately. it requires a change to all …
scbedd Oct 15, 2025
2ae6530
we are actually interacting with the proxy now. once per class fixtur…
scbedd Oct 15, 2025
8e4ad5c
we are actually booting the proxy and grabbing the port now
scbedd Oct 16, 2025
4de3571
going to need to regenerate pointing at http client unfortunately
scbedd Oct 16, 2025
94f676c
organize thoughts. time to finish some last details and then get to a…
scbedd Oct 16, 2025
5d4f5f8
initial usage of RecordingPathResolver
scbedd Oct 16, 2025
7df3a74
saving progress
scbedd Oct 16, 2025
32c195a
we have something working using the System.ClientModel generated spec…
scbedd Oct 17, 2025
3d4e949
enable debug mode
scbedd Oct 17, 2025
d55508b
progress update
scbedd Oct 17, 2025
6136811
successfully starting recording. time for delegate and submit for PR
scbedd Oct 20, 2025
f683320
Merge branch 'main' into support-record-playback-via-url-rewrite
scbedd Oct 22, 2025
b97f17f
add RecordingRedirectHandler, update HttpClientService to utilize it.…
scbedd Oct 22, 2025
121146a
cleaning up test mode. adding a couple static functions. time to inte…
scbedd Oct 22, 2025
8af0813
more integration fixes.
scbedd Oct 22, 2025
2d15c48
ensure that we use the injected httpclient in KeyVaultClient
scbedd Oct 22, 2025
6020f81
ensure that the base-uri that we pass in will work just fine
scbedd Oct 22, 2025
45a26fc
enable playback!
scbedd Oct 24, 2025
b91082d
broken state, but want to checkpoint before I turn chatgpt lose on a …
scbedd Oct 25, 2025
26bcdb4
finishing off the refactor to not re-implement a bunch of code
scbedd Oct 25, 2025
66eabb0
Merge branch 'main' into support-record-playback-via-url-rewrite
scbedd Oct 27, 2025
ff89ff2
target a version of the proxy that supports our necessary baseuri
scbedd Oct 27, 2025
9ffb1d6
successfully recorded all the default Azure Keyvault tests
scbedd Oct 27, 2025
c80c804
fixes for properly attaching TestProxy stderr to the failure output
scbedd Oct 28, 2025
7c2cb27
add safety around the output
scbedd Oct 28, 2025
8912415
download the client appropriately
scbedd Oct 28, 2025
0abcbad
fix a bit more around caching. time to re-integrate
scbedd Oct 28, 2025
dc83bbd
ensure we write the version.txt into downloaded proxy directory too
scbedd Oct 28, 2025
4c45da8
stabilization. removing previous references to feed. time to integrat…
scbedd Oct 28, 2025
9151ab4
fix up formatting
scbedd Oct 28, 2025
c7b6591
this _should_ cause CI to begin working
scbedd Oct 29, 2025
5e620a5
ok. NOW RecordedTestsBase should work
scbedd Oct 29, 2025
a1f2e20
move code changes behind #if DEBUG!
scbedd Oct 29, 2025
ebc6324
adjustments for only injecting redirect code on debug builds
scbedd Oct 29, 2025
e128dd4
apply formatting updates
scbedd Oct 29, 2025
8e575f0
Update tools/Azure.Mcp.Tools.KeyVault/src/Services/KeyVaultService.cs
scbedd Oct 29, 2025
bdd048c
deleted a useless test
scbedd Oct 29, 2025
5a0a052
Merge branch 'support-record-playback-via-url-rewrite' of https://git…
scbedd Oct 29, 2025
5097940
Merge branch 'main' into support-record-playback-via-url-rewrite
scbedd Oct 29, 2025
f07f26d
cleaning up around token credential. need to move the test mode to pu…
scbedd Oct 29, 2025
442693f
swap to testsettings test mode. TestProxyFixture still using environm…
scbedd Oct 29, 2025
b2b2442
use testmode everywhere instead of environment variable
scbedd Oct 29, 2025
e0b61b2
add TestMode to generated testsettings file. honor testmode from Live…
scbedd Oct 29, 2025
5d8a318
and now we properly honor TestMode! woot!
scbedd Oct 29, 2025
71ae164
remove useless warning disable in Azure.Mcp.Tests. clean up a couple …
scbedd Oct 30, 2025
e619616
Merge branch 'main' into support-record-playback-via-url-rewrite
scbedd Oct 30, 2025
60ed438
updating to PascalCase
scbedd Oct 30, 2025
e642a1c
add generation readme, remove eng/emitter-package.json
scbedd Oct 30, 2025
9df0a1a
update path that the file should be dropped under
scbedd Oct 30, 2025
2cbd769
properly update with file name
scbedd Oct 30, 2025
c181010
changes to support disabling default sanitizers, adding default sanit…
scbedd Oct 31, 2025
1aba28d
clean DisposeAsync. now we properly attach proxy stderr again when a …
scbedd Oct 31, 2025
d8e3429
add support for recording variables into RecordedCommandTestsBase. no…
scbedd Oct 31, 2025
8c8d0c3
fix issue with not saving variables in live mode
scbedd Oct 31, 2025
957bd10
ensure we don't throw when we shouldn't
scbedd Oct 31, 2025
ae278e9
merge main
scbedd Nov 3, 2025
b0a9813
resolve spelling by updating cspell dictionary + correcting spelling …
scbedd Nov 3, 2025
c71e82c
accepting PR feedback
scbedd Nov 3, 2025
b7b550a
remove nowarn addition
scbedd Nov 3, 2025
7af12a9
save progress, we're getting the recordingid available for overriding…
scbedd Nov 4, 2025
0584623
we're now able to set a custom matcher through an attribute.
scbedd Nov 4, 2025
aab710b
include updates for matcher
scbedd Nov 4, 2025
f43a353
merge main!
scbedd Nov 6, 2025
04bf1a9
pull across the matcher changes
scbedd Nov 9, 2025
b93ca9d
Merge branch 'main' into proxy-integration-tests-and-matcher
scbedd Nov 9, 2025
98745a4
we now support global and per-test override of sanitizers
scbedd Nov 9, 2025
904b1a5
commit stable. still need to do a BUNCH of cleanup of these tests, bu…
scbedd Nov 10, 2025
eb609d9
dotnet format. tests now passing. lets see how they handle CI
scbedd Nov 10, 2025
3f55337
some small changes to tests
scbedd Nov 10, 2025
aafc94a
remove the extra files that hav eno reason to exist here
scbedd Nov 11, 2025
10172fe
massive cleanup on the Test Proxy integration tests. eliminated code …
scbedd Nov 11, 2025
7943af6
cleanup some nitpicks that copilot caught
scbedd Nov 11, 2025
1290d49
apply formatting
scbedd Nov 11, 2025
76dc0c4
override LoadSettings() so that the new proxy integration tests ignor…
scbedd Nov 11, 2025
7f30055
sneaky little bug in how stuff was being called. the livetest setting…
scbedd Nov 11, 2025
d69978d
fix the issue with Settings not being initialized
scbedd Nov 11, 2025
7e6f4bd
somehow a build entirely failed. not certain how I msessed it up
scbedd Nov 11, 2025
1ae90ca
fix the downloading of the client on linux machines
scbedd Nov 11, 2025
9459a5a
formatting
scbedd Nov 11, 2025
97da555
revert wonky merge
scbedd Nov 11, 2025
07b4a84
Merge branch 'main' into proxy-integration-tests-and-matcher
scbedd Nov 11, 2025
f0b7b46
Merge branch 'proxy-integration-tests-and-matcher' into migrate-kv-to…
scbedd Nov 12, 2025
1d601dd
do not push yet.
scbedd Nov 12, 2025
b9d2773
fix warning as error. time to re-record
scbedd Nov 12, 2025
b2981b8
placing a static fake pfx such that the cer, csr, and values within b…
scbedd Nov 12, 2025
701ce5d
Merge branch 'main' into migrate-kv-to-recordings:
scbedd Nov 13, 2025
026b2d5
assets.json propagation
scbedd Nov 13, 2025
169dc67
adjust some defaults for the DisabledSanitizers.
scbedd Nov 13, 2025
a67bddd
Merge branch 'main' into migrate-kv-to-recordings
scbedd Nov 17, 2025
0f6eb7a
mac doesn't support EphemeralKeySet. need to propogate to intermediat…
scbedd Nov 17, 2025
22b0ec3
minor cleanup to keyvault
scbedd Nov 20, 2025
ce3fa8e
Update tools/Azure.Mcp.Tools.KeyVault/tests/Azure.Mcp.Tools.KeyVault.…
scbedd Nov 20, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ public abstract class RecordedCommandTestsBase(ITestOutputHelper output, TestPro
/// <summary>
/// The test-proxy has a default set of ~90 sanitizers for common sensitive data (GUIDs, tokens, timestamps, etc). This list allows opting out of specific default sanitizers by name.
/// Grab the names from the test-proxy source at https://github.com/Azure/azure-sdk-tools/blob/main/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Common/SanitizerDictionary.cs#L65)
/// Default Set:
/// - `AZSDK3430`: `$..id`
/// </summary>
public virtual List<string> DisabledDefaultSanitizers { get; } = new();
public virtual List<string> DisabledDefaultSanitizers { get; } = new() { "AZSDK3430" };

/// <summary>
/// During recording, variables saved to this dictionary will be propagated to the test-proxy and saved in the recording file.
Expand Down Expand Up @@ -343,7 +345,7 @@ private async Task StartRecordOrPlayback()
// Extract recording ID from response header
if (result.GetRawResponse().Headers.TryGetValue("x-recording-id", out var recordingId))
{
RecordingId = recordingId ?? String.Empty;
RecordingId = recordingId ?? string.Empty;
Output.WriteLine($"[Record] Recording ID: {RecordingId}");
}
}
Expand Down
55 changes: 44 additions & 11 deletions tools/Azure.Mcp.Tools.KeyVault/src/Services/KeyVaultService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
using Azure.Mcp.Core.Options;
using Azure.Mcp.Core.Services.Azure;
using Azure.Mcp.Core.Services.Azure.Tenant;
using Azure.Mcp.Core.Services.Http;
using Azure.Security.KeyVault.Administration;
using Azure.Security.KeyVault.Certificates;
using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Secrets;

namespace Azure.Mcp.Tools.KeyVault.Services;

public sealed class KeyVaultService(ITenantService tenantService) : BaseAzureService(tenantService), IKeyVaultService
public sealed class KeyVaultService(ITenantService tenantService, IHttpClientService httpClientService) : BaseAzureService(tenantService), IKeyVaultService
{
private readonly IHttpClientService _httpClientService = httpClientService ?? throw new ArgumentNullException(nameof(httpClientService));

public async Task<List<string>> ListKeys(
string vaultName,
bool includeManagedKeys,
Expand All @@ -24,7 +27,7 @@ public async Task<List<string>> ListKeys(
ValidateRequiredParameters((nameof(vaultName), vaultName), (nameof(subscriptionId), subscriptionId));

var credential = await GetCredential(tenantId, cancellationToken);
var client = new KeyClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateKeyClient(vaultName, credential, retryPolicy);
var keys = new List<string>();

try
Expand Down Expand Up @@ -53,7 +56,7 @@ public async Task<KeyVaultKey> GetKey(
ValidateRequiredParameters((nameof(vaultName), vaultName), (nameof(keyName), keyName), (nameof(subscriptionId), subscriptionId));

var credential = await GetCredential(tenantId, cancellationToken);
var client = new KeyClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateKeyClient(vaultName, credential, retryPolicy);

try
{
Expand All @@ -78,7 +81,7 @@ public async Task<KeyVaultKey> CreateKey(

var type = new KeyType(keyType);
var credential = await GetCredential(tenantId, cancellationToken);
var client = new KeyClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateKeyClient(vaultName, credential, retryPolicy);

try
{
Expand All @@ -100,7 +103,7 @@ public async Task<List<string>> ListSecrets(
ValidateRequiredParameters((nameof(vaultName), vaultName), (nameof(subscriptionId), subscriptionId));

var credential = await GetCredential(tenantId, cancellationToken);
var client = new SecretClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateSecretClient(vaultName, credential, retryPolicy);
var secrets = new List<string>();

try
Expand Down Expand Up @@ -130,7 +133,7 @@ public async Task<KeyVaultSecret> CreateSecret(
ValidateRequiredParameters((nameof(vaultName), vaultName), (nameof(secretName), secretName), (nameof(secretValue), secretValue), (nameof(subscriptionId), subscriptionId));

var credential = await GetCredential(tenantId, cancellationToken);
var client = new SecretClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateSecretClient(vaultName, credential, retryPolicy);

try
{
Expand All @@ -153,7 +156,7 @@ public async Task<KeyVaultSecret> GetSecret(
ValidateRequiredParameters((nameof(vaultName), vaultName), (nameof(secretName), secretName), (nameof(subscriptionId), subscriptionId));

var credential = await GetCredential(tenantId, cancellationToken);
var client = new SecretClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateSecretClient(vaultName, credential, retryPolicy);

try
{
Expand All @@ -176,7 +179,7 @@ public async Task<List<string>> ListCertificates(
ValidateRequiredParameters((nameof(vaultName), vaultName), (nameof(subscriptionId), subscriptionId));

var credential = await GetCredential(tenantId, cancellationToken);
var client = new CertificateClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateCertificateClient(vaultName, credential, retryPolicy);
var certificates = new List<string>();

try
Expand Down Expand Up @@ -205,7 +208,7 @@ public async Task<KeyVaultCertificateWithPolicy> GetCertificate(
ValidateRequiredParameters((nameof(vaultName), vaultName), (nameof(certificateName), certificateName), (nameof(subscriptionId), subscriptionId));

var credential = await GetCredential(tenantId, cancellationToken);
var client = new CertificateClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateCertificateClient(vaultName, credential, retryPolicy);

try
{
Expand All @@ -228,7 +231,7 @@ public async Task<CertificateOperation> CreateCertificate(
ValidateRequiredParameters((nameof(vaultName), vaultName), (nameof(certificateName), certificateName), (nameof(subscriptionId), subscriptionId));

var credential = await GetCredential(tenantId, cancellationToken);
var client = new CertificateClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateCertificateClient(vaultName, credential, retryPolicy);

try
{
Expand All @@ -253,7 +256,7 @@ public async Task<KeyVaultCertificateWithPolicy> ImportCertificate(
ValidateRequiredParameters((nameof(vaultName), vaultName), (nameof(certificateName), certificateName), (nameof(certificateData), certificateData), (nameof(subscriptionId), subscriptionId));

var credential = await GetCredential(tenantId, cancellationToken);
var client = new CertificateClient(new Uri($"https://{vaultName}.vault.azure.net"), credential);
var client = CreateCertificateClient(vaultName, credential, retryPolicy);

try
{
Expand Down Expand Up @@ -299,6 +302,36 @@ public async Task<KeyVaultCertificateWithPolicy> ImportCertificate(
}
}

private static Uri BuildVaultUri(string vaultName) => new($"https://{vaultName}.vault.azure.net");

// Create clients with injected HttpClient, this will enable record/playback during testing.
private KeyClient CreateKeyClient(string vaultName, Azure.Core.TokenCredential credential, RetryPolicyOptions? retry)
{
var httpClient = _httpClientService.CreateClient(BuildVaultUri(vaultName));
var options = new KeyClientOptions();
options = ConfigureRetryPolicy(AddDefaultPolicies(options), retry);
options.Transport = new Azure.Core.Pipeline.HttpClientTransport(httpClient);
return new KeyClient(BuildVaultUri(vaultName), credential, options);
}

private SecretClient CreateSecretClient(string vaultName, Azure.Core.TokenCredential credential, RetryPolicyOptions? retry)
{
var httpClient = _httpClientService.CreateClient(BuildVaultUri(vaultName));
var options = new SecretClientOptions();
options = ConfigureRetryPolicy(AddDefaultPolicies(options), retry);
options.Transport = new Azure.Core.Pipeline.HttpClientTransport(httpClient);
return new SecretClient(BuildVaultUri(vaultName), credential, options);
}

private CertificateClient CreateCertificateClient(string vaultName, Azure.Core.TokenCredential credential, RetryPolicyOptions? retry)
{
var httpClient = _httpClientService.CreateClient(BuildVaultUri(vaultName));
var options = new CertificateClientOptions();
options = ConfigureRetryPolicy(AddDefaultPolicies(options), retry);
options.Transport = new Azure.Core.Pipeline.HttpClientTransport(httpClient);
return new CertificateClient(BuildVaultUri(vaultName), credential, options);
}

public async Task<GetSettingsResult> GetVaultSettings(
string vaultName,
string subscription,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsTestProject>true</IsTestProject>
<OutputType>Exe</OutputType>
Expand All @@ -14,4 +14,9 @@
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="coverlet.collector" />
</ItemGroup>
<ItemGroup>
<None Update="TestResources\fake-pfx.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Loading