forked from Azure-Samples/key-vault-dotnet-recovery
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKeyVaultEntityRecoverySamples.cs
More file actions
246 lines (203 loc) · 10.8 KB
/
KeyVaultEntityRecoverySamples.cs
File metadata and controls
246 lines (203 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
using Azure;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Azure.Management.KeyVault.Fluent;
using Microsoft.Azure.Management.KeyVault.Fluent.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Rest.Azure;
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace AzureKeyVaultRecoverySamples
{
public sealed class KeyVaultEntityRecoverySamples : KeyVaultSampleBase
{
/// <summary>
/// Builds a vault recovery sample object with the specified parameters.
/// </summary>
/// <param name="tenantId">Tenant id.</param>
/// <param name="objectId">Object id of the Service Principal used to run the sample.</param>
/// <param name="appId">AD application id.</param>
/// <param name="appCredX5T">Thumbprint of the certificate set as the credential for the AD application.</param>
/// <param name="subscriptionId">Subscription id.</param>
/// <param name="resourceGroupName">Resource group name.</param>
/// <param name="vaultLocation">Location of the vault.</param>
/// <param name="vaultName">Vault name.</param>
public KeyVaultEntityRecoverySamples(string tenantId, string objectId, string appId, string appCredX5T, string subscriptionId, string resourceGroupName, string vaultLocation, string vaultName)
: base(tenantId, objectId, appId, appCredX5T, subscriptionId, resourceGroupName, vaultLocation, vaultName)
{ }
/// <summary>
/// Builds a vault recovery sample object from configuration.
/// </summary>
public KeyVaultEntityRecoverySamples()
: base()
{ }
#region samples
/// <summary>
/// Demonstrates how to enable soft delete on an existing vault, and then proceeds to delete, recover and purge the vault.
/// Assumes the caller has the KeyVaultContributor role in the subscription.
/// </summary>
/// <returns>Task representing this functionality.</returns>
public static async Task DemonstrateRecoveryAndPurgeAsync()
{
// instantiate the samples object
var sample = new KeyVaultEntityRecoverySamples();
var rgName = sample.context.ResourceGroupName;
// derive a unique vault name for this sample
var vaultName = sample.context.VaultName + "invault";
var secretName = "recoverysample";
// retrieve the vault (or create, if it doesn't exist)
var vault = await sample.CreateOrRetrieveVaultAsync(rgName, vaultName, enableSoftDelete: true, enablePurgeProtection: false);
var vaultUri = vault.Properties.VaultUri;
SecretClient secretClient = sample.GetDataClient(new Uri(vaultUri));
Console.WriteLine("Operating with vault name '{0}' in resource group '{1}' and location '{2}'", vaultName, rgName, vault.Location);
try
{
// set a secret
Console.Write("Setting a new value for secret '{0}'...", secretName);
await secretClient.SetSecretAsync(secretName, Guid.NewGuid().ToString());
Console.WriteLine("done.");
// confirm existence
Console.Write("Verifying secret creation...");
Response<KeyVaultSecret> retrievedSecretResponse = await secretClient.GetSecretAsync(secretName);
Console.WriteLine("done.");
// confirm recovery is possible
Console.Write("Verifying the secret deletion is recoverable...");
var recoveryLevel = retrievedSecretResponse.Value.Properties.RecoveryLevel;
if (!recoveryLevel.ToLowerInvariant().Contains("Recoverable".ToLowerInvariant()))
{
Console.WriteLine("failed; soft-delete is not enabled for this vault.");
return;
}
Console.WriteLine("done.");
// delete secret
Console.Write("Deleting secret...");
DeleteSecretOperation deleteSecretOperation = await secretClient.StartDeleteSecretAsync(secretName);
// When deleting a secret asynchronously before you purge it, you can await the WaitForCompletionAsync method on the operation
await deleteSecretOperation.WaitForCompletionAsync();
Console.WriteLine("done.");
// recover secret
Console.Write("Recovering deleted secret...");
RecoverDeletedSecretOperation recoverDeletedSecretOperation = await secretClient.StartRecoverDeletedSecretAsync(secretName);
await recoverDeletedSecretOperation.WaitForCompletionAsync();
Console.WriteLine("done.");
// confirm recovery
Console.Write("Retrieving recovered secret...");
await secretClient.GetSecretAsync(secretName);
Console.WriteLine("done.");
// delete secret
Console.Write("Deleting recorvered secret...");
DeleteSecretOperation deleteRecoveredSecretOperation = await secretClient.StartDeleteSecretAsync(secretName);
await deleteRecoveredSecretOperation.WaitForCompletionAsync();
Console.WriteLine("done.");
// retrieve deleted secret
Console.Write("Retrieving the deleted secret...");
await secretClient.GetDeletedSecretAsync(secretName);
Console.WriteLine("done.");
}
catch (RequestFailedException ex)
{
Console.WriteLine("Unexpected Key Vault exception encountered: {0}", ex.Message);
throw;
}
catch (Exception e)
{
Console.WriteLine("Unexpected exception encountered: {0}", e.Message);
throw;
}
}
/// <summary>
/// Demonstrates how to back up and restore a secret.
/// </summary>
/// <returns>Task representing this functionality.</returns>
public static async Task DemonstrateBackupAndRestoreAsync()
{
// instantiate the samples object
var sample = new KeyVaultEntityRecoverySamples();
var rgName = sample.context.ResourceGroupName;
// derive a unique vault name for this sample
var vaultName = sample.context.VaultName + "backuprestore";
var secretName = "backupsample";
// retrieve the vault (or create, if it doesn't exist)
var vault = await sample.CreateOrRetrieveVaultAsync(rgName, vaultName, enableSoftDelete: false, enablePurgeProtection: false);
var vaultUri = vault.Properties.VaultUri;
SecretClient secretClient = sample.GetDataClient(new Uri(vaultUri));
Console.WriteLine("Operating with vault name '{0}' in resource group '{1}' and location '{2}'", vaultName, rgName, vault.Location);
try
{
// set a secret
Console.Write("Setting a new value for secret '{0}'...", secretName);
await secretClient.SetSecretAsync(secretName, Guid.NewGuid().ToString());
Console.WriteLine("done.");
// confirm existence
Console.Write("Verifying secret creation...");
await secretClient.GetSecretAsync(secretName);
Console.WriteLine("done.");
// backup secret
Console.Write("Backing up secret...");
Response<byte[]> backupResponse = await secretClient.BackupSecretAsync(secretName);
Console.WriteLine("done.");
// delete secret
Console.Write("Deleting secret...");
DeleteSecretOperation deleteSecretOperation = await secretClient.StartDeleteSecretAsync(secretName);
// When deleting a secret asynchronously before you purge it, you can await the WaitForCompletionAsync method on the operation
await deleteSecretOperation.WaitForCompletionAsync();
Console.WriteLine("done.");
// restore secret
Console.Write("Restoring secret from backup...");
await secretClient.RestoreSecretBackupAsync(backupResponse.Value);
Console.WriteLine("done.");
// confirm existence
Console.Write("Verifying secret restoration...");
await secretClient.GetSecretAsync(secretName);
Console.WriteLine("done.");
}
catch (RequestFailedException ex)
{
Console.WriteLine("Unexpected Key Vault exception encountered: {0}", ex.Message);
throw;
}
catch (Exception e)
{
Console.WriteLine("Unexpected exception encountered: {0}", e.Message);
throw;
}
}
#endregion
#region helpers
private async Task<VaultInner> CreateOrRetrieveVaultAsync(string resourceGroupName, string vaultName, bool enableSoftDelete, bool enablePurgeProtection)
{
VaultInner vault = null;
try
{
// check whether the vault exists
Console.Write("Checking the existence of the vault...");
vault = await ManagementClient.Vaults.GetAsync(resourceGroupName, vaultName).ConfigureAwait(false);
Console.WriteLine("done.");
}
catch (CloudException ce)
{
if (ce.Response.StatusCode != HttpStatusCode.NotFound)
{
Console.WriteLine("Unexpected exception encountered retrieving the vault: {0}", ce.Message);
throw;
}
// create a new vault
var vaultParameters = CreateVaultParameters(resourceGroupName, vaultName, context.PreferredLocation, enableSoftDelete, enablePurgeProtection);
// create new soft-delete-enabled vault
Console.Write("Vault does not exist; creating...");
vault = await ManagementClient.Vaults.CreateOrUpdateAsync(resourceGroupName, vaultName, vaultParameters).ConfigureAwait(false);
Console.WriteLine("done.");
// wait for the DNS record to propagate; verify properties
Console.Write("Waiting for DNS propagation..");
Thread.Sleep(10 * 1000);
Console.WriteLine("done.");
Console.Write("Retrieving newly created vault...");
vault = await ManagementClient.Vaults.GetAsync(resourceGroupName, vaultName).ConfigureAwait(false);
Console.WriteLine("done.");
}
return vault;
}
#endregion
}
}