Skip to content

Commit 5351298

Browse files
authored
Add migration guide for Key Vault secrets (Azure#15118)
* Add migration guide for Key Vault secrets Relates to Azure#12108
1 parent eae6c13 commit 5351298

File tree

4 files changed

+444
-1
lines changed

4 files changed

+444
-1
lines changed
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# Migrate from Microsoft.Azure.KeyVault to Azure.Security.KeyVault.Secrets
2+
3+
This guide is intended to assist in the migration to version 4 of the Key Vault client library [`Azure.Security.KeyVault.Secrets`](https://www.nuget.org/packages/Azure.Security.KeyVault.Secrets) from version 3 of [`Microsoft.Azure.KeyVault`](https://www.nuget.org/packages/Microsoft.Azure.KeyVault). It will focus on side-by-side comparisons for similar operations between the two packages.
4+
5+
Familiarity with the `Microsoft.Azure.KeyVault` library is assumed. For those new to the Key Vault client library for .NET, please refer to the [`Azure.Security.KeyVault.Secrets` README](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Secrets/README.md) and [`Azure.Security.KeyVault.Secrets` samples](https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/keyvault/Azure.Security.KeyVault.Secrets/samples) for the `Azure.Security.KeyVault.Secrets` library rather than this guide.
6+
7+
## Table of contents
8+
9+
- [Migration benefits](#migration-benefits)
10+
- [General changes](#general-changes)
11+
- [Package and namespaces](#package-and-namespaces)
12+
- [Separate clients](#separate-clients)
13+
- [Client constructors](#client-constructors)
14+
- [Setting secrets](#setting-secrets)
15+
- [Getting secrets](#getting-secrets)
16+
- [Listing secrets](#listing-secrets)
17+
- [Deleting secrets](#deleting-secrets)
18+
- [Managing shared access signatures](#managing-shared-access-signatures)
19+
- [Additional samples](#additional-samples)
20+
21+
## Migration benefits
22+
23+
A natural question to ask when considering whether or not to adopt a new version or library is what the benefits of doing so would be. As Azure has matured and been embraced by a more diverse group of developers, we have been focused on learning the patterns and practices to best support developer productivity and to understand the gaps that the .NET client libraries have.
24+
25+
There were several areas of consistent feedback expressed across the Azure client library ecosystem. One of the most important is that the client libraries for different Azure services have not had a consistent approach to organization, naming, and API structure. Additionally, many developers have felt that the learning curve was difficult, and the APIs did not offer a good, approachable, and consistent onboarding story for those learning Azure or exploring a specific Azure service.
26+
27+
To try and improve the development experience across Azure services, including Key Vault, a set of uniform [design guidelines](https://azure.github.io/azure-sdk/general_introduction.html) was created for all languages to drive a consistent experience with established API patterns for all services. A set of [.NET-specific guidelines](https://azure.github.io/azure-sdk/dotnet_introduction.html) was also introduced to ensure that .NET clients have a natural and idiomatic feel that mirrors that of the .NET base class libraries. Further details are available in the guidelines for those interested.
28+
29+
The new Key Vault secrets library `Azure.Security.KeyVault.Secrets` provides the ability to share in some of the cross-service improvements made to the Azure development experience, such as using the new `Azure.Identity` library to share a single authentication between clients and a unified diagnostics pipeline offering a common view of the activities across each of the client libraries.
30+
31+
## General changes
32+
33+
### Package and namespaces
34+
35+
Package names and the namespace root for the modern Azure client libraries for .NET have changed. Each will follow the pattern `Azure.[Area].[Services]` where the legacy clients followed the pattern `Microsoft.Azure.[Service]`. This provides a quick and accessible means to help understand, at a glance, whether you are using the modern or legacy clients.
36+
37+
In the case of Key Vault, the modern client libraries have packages and namespaces that begin with `Azure.Security.KeyVault` and were released beginning with version 4. The legacy client libraries have packages and namespaces that begin with `Microsoft.Azure.KeyVault` and a version of 3.x.x or below.
38+
39+
### Separate clients
40+
41+
In the interest of simplifying the API we've split `KeyVaultClient` into separate packages and clients:
42+
43+
- `Azure.Security.KeyVault.Certificates` contains `CertificateClient` for working with certificates.
44+
- `Azure.Security.KeyVault.Keys` contains `KeyClient` for working with keys and `CryptographyClient` for performing cryptographic operations.
45+
- `Azure.Security.KeyVault.Secrets` contains `SecretClient` for working with secrets.
46+
47+
These clients also share a single connection pool by default despite being separated, resolving an issue with the old `KeyVaultClient` that created a new connection pool with each new instance and could exhaust socket connections.
48+
49+
### Client constructors
50+
51+
Across all new Azure client libraries, clients consistently take an endpoint or connection string along with token credentials. This differs from `KeyVaultClient` that took an authentication delegate and could be used for multiple Key Vault endpoints.
52+
53+
#### Authenticating
54+
55+
Previously in `Microsoft.Azure.KeyVault`, you could create a `KeyVaultClient` along with the `AzureServiceTokenProvider` from the package `Microsoft.Azure.Services.AppAuthentication`:
56+
57+
```C# Snippet:Microsoft_Azure_KeyVault_Snippets_MigrationGuide_Create
58+
AzureServiceTokenProvider provider = new AzureServiceTokenProvider();
59+
KeyVaultClient client = new KeyVaultClient(
60+
new KeyVaultClient.AuthenticationCallback(provider.KeyVaultTokenCallback));
61+
```
62+
63+
Now in `Azure.Security.KeyVault.Secrets`, you create a `SecretClient` along with the `DefaultAzureCredential` from the package `Azure.Identity`:
64+
65+
```C# Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_Create
66+
SecretClient client = new SecretClient(
67+
new Uri("https://myvault.vault.azure.net"),
68+
new DefaultAzureCredential());
69+
```
70+
71+
[`DefaultAzureCredential`](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/README.md#defaultazurecredential) is optimized for both production and development environments without having to change your source code.
72+
73+
#### Sharing an HttpClient
74+
75+
In `Microsoft.Azure.KeyVault` with `KeyVaultClient`, a new `HttpClient` was created with each instance but could be shared to prevent connection starvation:
76+
77+
```C# Snippet:Microsoft_Azure_KeyVault_Snippets_MigrationGuide_CreateWithOptions
78+
using (HttpClient httpClient = new HttpClient())
79+
{
80+
AzureServiceTokenProvider provider = new AzureServiceTokenProvider();
81+
KeyVaultClient client = new KeyVaultClient(
82+
new KeyVaultClient.AuthenticationCallback(provider.KeyVaultTokenCallback),
83+
httpClient);
84+
}
85+
```
86+
87+
In `Azure.Security.KeyVault.Secrets` by default, all client libraries built on `Azure.Core` that communicate over HTTP share a single `HttpClient`. If you want to share an your own `HttpClient` instance with Azure client libraries and other clients you use or implement in your projects, you can pass it via `SecretClientOptions`:
88+
89+
```C# Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_CreateWithOptions
90+
using (HttpClient httpClient = new HttpClient())
91+
{
92+
SecretClientOptions options = new SecretClientOptions
93+
{
94+
Transport = new HttpClientTransport(httpClient)
95+
};
96+
97+
SecretClient client = new SecretClient(
98+
new Uri("https://myvault.vault.azure.net"),
99+
new DefaultAzureCredential(),
100+
options);
101+
}
102+
```
103+
104+
[`ClientOptions`](https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-usage-options) classes are another common feature of Azure client libraries to configure clients, including diagnostics, retry options, and transport options including your pipeline policies.
105+
106+
### Setting secrets
107+
108+
Previously in `Microsoft.Azure.KeyVault`, you could set a secret value using the `KeyVaultClient` and a specific Key Vault endpoint:
109+
110+
```C# Snippet:Microsoft_Azure_KeyVault_Snippets_MigrationGuide_SetSecret
111+
SecretBundle secret = await client.SetSecretAsync("https://myvault.vault.azure.net", "secret-name", "secret-value");
112+
```
113+
114+
Now in `Azure.Security.KeyVault.Secrets`, you set a secret value in the Key Vault you specified when constructing the `SecretClient`:
115+
116+
```C# Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_SetSecret
117+
KeyVaultSecret secret = await client.SetSecretAsync("secret-name", "secret-value");
118+
```
119+
120+
Setting an existing secret in both cases will create a new version of the secret.
121+
122+
Synchronous methods are also available on `SecretClient`, though we recommend you use asynchronous methods throughout your projects when possible for better performing applications.
123+
124+
### Getting secrets
125+
126+
Previously in `Microsoft.Azure.KeyVault`, you could get a secret value using the `KeyVaultClient` and a specific Key Vault endpoint:
127+
128+
```C# Snippet:Microsoft_Azure_KeyVault_Snippets_MigrationGuide_GetSecret
129+
// Get the latest secret value.
130+
SecretBundle secret = await client.GetSecretAsync("https://myvault.vault.azure.net", "secret-name", null);
131+
132+
// Get a specific secret value.
133+
SecretBundle secretVersion = await client.GetSecretAsync("https://myvault.vault.azure.net", "secret-name", "e43af03a7cbc47d4a4e9f11540186048");
134+
```
135+
136+
Now in `Azure.Security.KeyVault.Secrets`, you get a secret value in the Key Vault you specified when constructing the `SecretClient`:
137+
138+
```C# Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_GetSecret
139+
// Get the latest secret value.
140+
KeyVaultSecret secret = await client.GetSecretAsync("secret-name");
141+
142+
// Get a specific secret value.
143+
KeyVaultSecret secretVersion = await client.GetSecretAsync("secret-name", "e43af03a7cbc47d4a4e9f11540186048");
144+
```
145+
146+
Synchronous methods are also available on `SecretClient`, though we recommend you use asynchronous methods throughout your projects when possible for better performing applications.
147+
148+
### Listing secrets
149+
150+
Previously in `Microsoft.Azure.KeyVault`, you could list secrets' properties using the `KeyVaultClient` and a specific Key Vault endpoint:
151+
152+
```C# Snippet:Microsoft_Azure_KeyVault_Snippets_MigrationGuide_ListSecrets
153+
IPage<SecretItem> page = await client.GetSecretsAsync("https://myvault.vault.azure.net");
154+
foreach (SecretItem item in page)
155+
{
156+
SecretIdentifier secretId = item.Identifier;
157+
SecretBundle secret = await client.GetSecretAsync(secretId.Vault, secretId.Name);
158+
}
159+
160+
while (page.NextPageLink != null)
161+
{
162+
page = await client.GetSecretsNextAsync(page.NextPageLink);
163+
foreach (SecretItem item in page)
164+
{
165+
SecretIdentifier secretId = item.Identifier;
166+
SecretBundle secret = await client.GetSecretAsync(secretId.Vault, secretId.Name);
167+
}
168+
}
169+
```
170+
171+
Now in `Azure.Security.KeyVault.Secrets`, you list secrets' properties in the Key Vault you specified when constructing the `SecretClient`. This returns an enumerable that enumerates all secrets across any number of pages. If you want to enumerate pages, call the `AsPages` method on the returned enumerable.
172+
173+
```C# Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_ListSecrets
174+
// List all secrets asynchronously.
175+
await foreach (SecretProperties item in client.GetPropertiesOfSecretsAsync())
176+
{
177+
KeyVaultSecret secret = await client.GetSecretAsync(item.Name);
178+
}
179+
180+
// List all secrets synchronously.
181+
foreach (SecretProperties item in client.GetPropertiesOfSecrets())
182+
{
183+
KeyVaultSecret secret = client.GetSecret(item.Name);
184+
}
185+
```
186+
187+
### Deleting secrets
188+
189+
Previously in `Microsoft.Azure.KeyVault`, you could delete a secret using the `KeyVaultClient` and a specific Key Vault endpoint:
190+
191+
```C# Snippet:Microsoft_Azure_KeyVault_Snippets_MigrationGuide_DeleteSecret
192+
// Delete the secret.
193+
DeletedSecretBundle deletedSecret = await client.DeleteSecretAsync("https://myvault.vault.azure.net", "secret-name");
194+
195+
// Purge or recover the deleted secret if soft delete is enabled.
196+
if (deletedSecret.RecoveryId != null)
197+
{
198+
DeletedSecretIdentifier deletedSecretId = deletedSecret.RecoveryIdentifier;
199+
200+
// Deleting a secret does not happen immediately. Wait a while and check if the deleted secret exists.
201+
while (true)
202+
{
203+
try
204+
{
205+
await client.GetDeletedSecretAsync(deletedSecretId.Vault, deletedSecretId.Name);
206+
207+
// Finally deleted.
208+
break;
209+
}
210+
catch (KeyVaultErrorException ex) when (ex.Response.StatusCode == HttpStatusCode.NotFound)
211+
{
212+
// Not yet deleted...
213+
}
214+
}
215+
216+
// Purge the deleted secret.
217+
await client.PurgeDeletedSecretAsync(deletedSecretId.Vault, deletedSecretId.Name);
218+
219+
// You can also recover the deleted secret using RecoverDeletedSecretAsync.
220+
}
221+
```
222+
223+
Now in `Azure.Security.KeyVault.Secrets`, you delete a secret in the Key Vault you specified when constructing the `SecretClient` and succinctly await or poll status on an operation to complete:
224+
225+
```C# Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_DeleteSecret
226+
// Delete the secret.
227+
DeleteSecretOperation deleteOperation = await client.StartDeleteSecretAsync("secret-name");
228+
229+
// Purge or recover the deleted secret if soft delete is enabled.
230+
if (deleteOperation.Value.RecoveryId != null)
231+
{
232+
// Deleting a secret does not happen immediately. Wait for the secret to be deleted.
233+
DeletedSecret deletedSecret = await deleteOperation.WaitForCompletionAsync();
234+
235+
// Purge the deleted secret.
236+
await client.PurgeDeletedSecretAsync(deletedSecret.Name);
237+
238+
// You can also recover the deleted secret using StartRecoverDeletedSecretAsync,
239+
// which returns RecoverDeletedSecretOperation you can await like DeleteSecretOperation above.
240+
}
241+
```
242+
243+
Synchronous methods are also available on `SecretClient`, though we recommend you use asynchronous methods throughout your projects when possible for better performing applications.
244+
245+
### Managing shared access signatures
246+
247+
Because [Role-Based Access Control (RBAC)](https://docs.microsoft.com/azure/role-based-access-control/overview) is now recommended for storage account access control, the APIs for Key Vault-managed storage accounts are no longer available in version 4 of Key Vault client libraries. If you cannot use RBAC and must use [Shared Access Signatures (SAS)](https://docs.microsoft.com/azure/storage/common/storage-sas-overview), see [our sample](https://docs.microsoft.com/samples/azure/azure-sdk-for-net/share-link/) for source you can use in your own projects built on the same `Azure.Core` pipeline as the version 4 client libraries described above.
248+
249+
## Additional samples
250+
251+
- [Key Vault secrets samples for .NET](https://docs.microsoft.com/samples/azure/azure-sdk-for-net/azuresecuritykeyvaultsecrets-samples/)
252+
- [All Key Vault samples for .NET](https://docs.microsoft.com/samples/browse/?products=azure-key-vault&languages=csharp)

sdk/keyvault/Azure.Security.KeyVault.Secrets/tests/samples/SampleSnippets.cs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using Azure.Core.Pipeline;
45
using Azure.Identity;
56
using NUnit.Framework;
67
using System;
78
using System.Threading;
89
using System.Threading.Tasks;
9-
using Azure.Security.KeyVault.Tests;
10+
using System.Net.Http;
1011

1112
namespace Azure.Security.KeyVault.Secrets.Samples
1213
{
@@ -187,5 +188,83 @@ public void DeleteAndPurgeSecret()
187188
client.PurgeDeletedSecret(secret.Name);
188189
#endregion
189190
}
191+
192+
[Ignore("Used only for the migration guide")]
193+
private async Task MigrationGuide()
194+
{
195+
#region Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_Create
196+
SecretClient client = new SecretClient(
197+
new Uri("https://myvault.vault.azure.net"),
198+
new DefaultAzureCredential());
199+
#endregion Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_Create
200+
201+
#region Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_CreateWithOptions
202+
using (HttpClient httpClient = new HttpClient())
203+
{
204+
SecretClientOptions options = new SecretClientOptions
205+
{
206+
Transport = new HttpClientTransport(httpClient)
207+
};
208+
209+
//@@SecretClient client = new SecretClient(
210+
/*@@*/ SecretClient _ = new SecretClient(
211+
new Uri("https://myvault.vault.azure.net"),
212+
new DefaultAzureCredential(),
213+
options);
214+
}
215+
#endregion Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_CreateWithOptions
216+
217+
{
218+
#region Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_SetSecret
219+
KeyVaultSecret secret = await client.SetSecretAsync("secret-name", "secret-value");
220+
#endregion Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_SetSecret
221+
}
222+
223+
{
224+
#region Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_GetSecret
225+
// Get the latest secret value.
226+
KeyVaultSecret secret = await client.GetSecretAsync("secret-name");
227+
228+
// Get a specific secret value.
229+
KeyVaultSecret secretVersion = await client.GetSecretAsync("secret-name", "e43af03a7cbc47d4a4e9f11540186048");
230+
#endregion Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_GetSecret
231+
}
232+
233+
{
234+
#region Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_ListSecrets
235+
// List all secrets asynchronously.
236+
await foreach (SecretProperties item in client.GetPropertiesOfSecretsAsync())
237+
{
238+
KeyVaultSecret secret = await client.GetSecretAsync(item.Name);
239+
}
240+
241+
// List all secrets synchronously.
242+
foreach (SecretProperties item in client.GetPropertiesOfSecrets())
243+
{
244+
KeyVaultSecret secret = client.GetSecret(item.Name);
245+
}
246+
#endregion Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_ListSecrets
247+
}
248+
249+
{
250+
#region Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_DeleteSecret
251+
// Delete the secret.
252+
DeleteSecretOperation deleteOperation = await client.StartDeleteSecretAsync("secret-name");
253+
254+
// Purge or recover the deleted secret if soft delete is enabled.
255+
if (deleteOperation.Value.RecoveryId != null)
256+
{
257+
// Deleting a secret does not happen immediately. Wait for the secret to be deleted.
258+
DeletedSecret deletedSecret = await deleteOperation.WaitForCompletionAsync();
259+
260+
// Purge the deleted secret.
261+
await client.PurgeDeletedSecretAsync(deletedSecret.Name);
262+
263+
// You can also recover the deleted secret using StartRecoverDeletedSecretAsync,
264+
// which returns RecoverDeletedSecretOperation you can await like DeleteSecretOperation above.
265+
}
266+
#endregion Snippet:Azure_Security_KeyVault_Secrets_Snippets_MigrationGuide_DeleteSecret
267+
}
268+
}
190269
}
191270
}

sdk/keyvault/Microsoft.Azure.KeyVault/tests/Microsoft.Azure.KeyVault.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
</ItemGroup>
2828

2929
<ItemGroup>
30+
<PackageReference Include="Microsoft.Azure.Services.AppAuthentication" />
3031
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" VersionOverride="[3.14.2, 4.0.0)" />
3132
</ItemGroup>
3233

0 commit comments

Comments
 (0)