Skip to content
This repository was archived by the owner on Jul 19, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions AzureKeyVaultRecoverySamples.csproj
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.0" />
<PackageReference Include="Azure.Identity" Version="1.2.3" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.1.0" />
<PackageReference Include="Microsoft.Azure.Management.KeyVault.Fluent" Version="1.6.0" />
<PackageReference Include="Microsoft.Azure.Management.ResourceManager.Fluent" Version="1.6.0" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.1" />
Expand Down
42 changes: 5 additions & 37 deletions ClientContext.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System;
using System.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Rest;
using Microsoft.Rest.Azure.Authentication;
using System;
using System.Threading.Tasks;

namespace AzureKeyVaultRecoverySamples
{
Expand Down Expand Up @@ -60,7 +58,7 @@ public static ClientContext Build(string tenantId, string objectId, string appId
/// </summary>
/// <param name="certificateThumbprint"></param>
/// <returns></returns>
public static Task<ServiceClientCredentials> GetServiceCredentialsAsync( string tenantId, string applicationId, string appSecret )
public static Task<ServiceClientCredentials> GetServiceCredentialsAsync(string tenantId, string applicationId, string appSecret)
{
if (_servicePrincipalCredential == null)
{
Expand All @@ -69,41 +67,11 @@ public static Task<ServiceClientCredentials> GetServiceCredentialsAsync( string

return ApplicationTokenProvider.LoginSilentAsync(
tenantId,
_servicePrincipalCredential,
_servicePrincipalCredential,
ActiveDirectoryServiceSettings.Azure,
TokenCache.DefaultShared);
}

/// <summary>
/// Generic ADAL Authentication callback
/// </summary>
public static async Task<string> AcquireTokenAsync(string authority, string resource, string scope)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To track 2, there is new SecretClient instead of KeyVaultClient to use. this AcquireTokenAsync() is used to create KeyVaultClient in KeyvaultSampleBase delegate. So this is decided to remove

{
if (_servicePrincipalCredential == null)
{
// read directly from config
var appId = ConfigurationManager.AppSettings[SampleConstants.ConfigKeys.ApplicationId];
var spSecret = ConfigurationManager.AppSettings[SampleConstants.ConfigKeys.SPSecret];

_servicePrincipalCredential = new ClientCredential(appId, spSecret);
}

AuthenticationContext ctx = new AuthenticationContext(authority, false, TokenCache.DefaultShared);
AuthenticationResult result = await ctx.AcquireTokenAsync(resource, _servicePrincipalCredential).ConfigureAwait(false);

return result.AccessToken;
}

/// <summary>
/// Generic authentication callback for a specific tenant
/// </summary>
/// <param name="tenantId">Identifier of tenant where authentication takes place.</param>
/// <returns>Authentication callback.</returns>
/// <remarks>Consider moving this class out from Controllers.Core into a separate top-level lib.</remarks>
public static Func<Task<string>> GetAuthenticationCallback(string authority, string resource, string scope)
{
return () => { return AcquireTokenAsync(authority, resource, scope); };
}
#endregion
}
}
99 changes: 39 additions & 60 deletions KeyVaultEntityRecoverySamples.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.KeyVault.Models;
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
{
Expand Down Expand Up @@ -54,23 +55,26 @@ public static async Task DemonstrateRecoveryAndPurgeAsync()
// 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);
var secretResponse = await sample.DataClient.SetSecretWithHttpMessagesAsync(vaultUri, secretName, Guid.NewGuid().ToString()).ConfigureAwait(false);
await secretClient.SetSecretAsync(secretName, Guid.NewGuid().ToString());
Console.WriteLine("done.");

// confirm existence
Console.Write("Verifying secret creation...");
var retrievedSecretResponse = await sample.DataClient.GetSecretWithHttpMessagesAsync(vaultUri, secretName, secretVersion: String.Empty).ConfigureAwait(false);
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.Body.Attributes.RecoveryLevel;
var recoveryLevel = retrievedSecretResponse.Value.Properties.RecoveryLevel;
if (!recoveryLevel.ToLowerInvariant().Contains("Recoverable".ToLowerInvariant()))
{
Console.WriteLine("failed; soft-delete is not enabled for this vault.");
Expand All @@ -79,64 +83,41 @@ public static async Task DemonstrateRecoveryAndPurgeAsync()
}
Console.WriteLine("done.");


// delete secret
Console.Write("Deleting secret...");
await sample.DataClient.DeleteSecretWithHttpMessagesAsync(vaultUri, secretName).ConfigureAwait(false);
Console.WriteLine("done.");
DeleteSecretOperation deleteSecretOperation = await secretClient.StartDeleteSecretAsync(secretName);

// retrieve deleted secret; recoverable deletion is an asynchronous operation, during which the secret
// is not accessible, either as an active entity or a deleted one. Polling for up to 45s should be sufficient.
Console.Write("Retrieving the deleted secret...");
AzureOperationResponse<DeletedSecretBundle> deletedSecretResponse = null;
await RetryHttpRequestAsync(
async () => { return deletedSecretResponse = await sample.DataClient.GetDeletedSecretWithHttpMessagesAsync(vaultUri, secretName).ConfigureAwait(false); },
"get deleted secret",
SampleConstants.RetryPolicies.DefaultSoftDeleteRetryPolicy)
.ConfigureAwait(false);
// 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...");
var recoveredSecretResponse = await sample.DataClient.RecoverDeletedSecretWithHttpMessagesAsync(vaultUri, secretName).ConfigureAwait(false);
RecoverDeletedSecretOperation recoverDeletedSecretOperation = await secretClient.StartRecoverDeletedSecretAsync(secretName);
await recoverDeletedSecretOperation.WaitForCompletionAsync();
Console.WriteLine("done.");

// confirm recovery
Console.Write("Retrieving recovered secret...");
await RetryHttpRequestAsync(
async () => { return retrievedSecretResponse = await sample.DataClient.GetSecretWithHttpMessagesAsync(vaultUri, secretName, secretVersion: String.Empty).ConfigureAwait(false); },
"recover deleted secret",
SampleConstants.RetryPolicies.DefaultSoftDeleteRetryPolicy)
.ConfigureAwait(false);
await secretClient.GetSecretAsync(secretName);
Console.WriteLine("done.");

// delete secret
Console.Write("Deleting secret (pass #2)...");
await sample.DataClient.DeleteSecretWithHttpMessagesAsync(vaultUri, secretName).ConfigureAwait(false);
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 (pass #2)...");
await RetryHttpRequestAsync(
async () => { return deletedSecretResponse = await sample.DataClient.GetDeletedSecretWithHttpMessagesAsync(vaultUri, secretName); },
"get deleted secret",
SampleConstants.RetryPolicies.DefaultSoftDeleteRetryPolicy)
.ConfigureAwait(false);
Console.WriteLine("done.");

// purge secret
Console.Write("Purging deleted secret...");
await sample.DataClient.PurgeDeletedSecretWithHttpMessagesAsync(vaultUri, secretName).ConfigureAwait(false);
Console.Write("Retrieving the deleted secret...");
await secretClient.GetDeletedSecretAsync(secretName);
Console.WriteLine("done.");
}
catch (KeyVaultErrorException kvee)
{
Console.WriteLine("Unexpected KeyVault exception encountered: {0}", kvee.Message);
catch (RequestFailedException ex)

throw;
}
catch (CloudException ce)
{
Console.WriteLine("Unexpected ARM exception encountered: {0}", ce.Message);
Console.WriteLine("Unexpected Key Vault exception encountered: {0}", ex.Message);

throw;
}
Expand Down Expand Up @@ -166,50 +147,48 @@ public static async Task DemonstrateBackupAndRestoreAsync()
// 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);
var secretResponse = await sample.DataClient.SetSecretWithHttpMessagesAsync(vaultUri, secretName, Guid.NewGuid().ToString()).ConfigureAwait(false);
await secretClient.SetSecretAsync(secretName, Guid.NewGuid().ToString());
Console.WriteLine("done.");

// confirm existence
Console.Write("Verifying secret creation...");
var retrievedSecretResponse = await sample.DataClient.GetSecretWithHttpMessagesAsync(vaultUri, secretName, secretVersion: String.Empty).ConfigureAwait(false);
await secretClient.GetSecretAsync(secretName);
Console.WriteLine("done.");

// backup secret
Console.Write("Backing up secret...");
var backupResponse = await sample.DataClient.BackupSecretWithHttpMessagesAsync(vaultUri, secretName).ConfigureAwait(false);
Response<byte[]> backupResponse = await secretClient.BackupSecretAsync(secretName);
Console.WriteLine("done.");

// delete secret
Console.Write("Deleting secret...");
await sample.DataClient.DeleteSecretWithHttpMessagesAsync(vaultUri, secretName).ConfigureAwait(false);
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...");
byte[] secretBackup = backupResponse.Body.Value;
var restoreResponse = await sample.DataClient.RestoreSecretWithHttpMessagesAsync(vaultUri, secretBackup).ConfigureAwait(false);
await secretClient.RestoreSecretBackupAsync(backupResponse.Value);
Console.WriteLine("done.");

// confirm existence
Console.Write("Verifying secret restoration...");
retrievedSecretResponse = await sample.DataClient.GetSecretWithHttpMessagesAsync(vaultUri, secretName, secretVersion: String.Empty).ConfigureAwait(false);
await secretClient.GetSecretAsync(secretName);
Console.WriteLine("done.");
}
catch (KeyVaultErrorException kvee)
{
Console.WriteLine("Unexpected KeyVault exception encountered: {0}", kvee.Message);

throw;
}
catch (CloudException ce)
catch (RequestFailedException ex)
{
Console.WriteLine("Unexpected ARM exception encountered: {0}", ce.Message);
Console.WriteLine("Unexpected Key Vault exception encountered: {0}", ex.Message);

throw;
}
Expand Down
12 changes: 6 additions & 6 deletions KeyVaultRecoverySamples.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System;
using Microsoft.Azure.Management.KeyVault.Fluent;
using Microsoft.Azure.Management.KeyVault.Fluent.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Management.KeyVault.Fluent;
using Microsoft.Azure.Management.KeyVault.Fluent.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;

namespace AzureKeyVaultRecoverySamples
{
Expand All @@ -25,7 +25,7 @@ public sealed class KeyVaultRecoverySamples : KeyVaultSampleBase
/// <param name="resourceGroupName">Resource group name.</param>
/// <param name="vaultLocation">Location of the vault.</param>
/// <param name="vaultName">Vault name.</param>
public KeyVaultRecoverySamples( string tenantId, string objectId, string appId, string appCredX5T, string subscriptionId, string resourceGroupName, string vaultLocation, string vaultName )
public KeyVaultRecoverySamples(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)
{ }

Expand Down Expand Up @@ -79,7 +79,7 @@ public static async Task DemonstrateRecoveryAndPurgeForNewVaultAsync()
Console.WriteLine("done.");

// confirm the existence of the deleted vault
Console.Write("Retrieving deleted vault...");
Console.Write("Retrieving deleted vault...");
deletedVault = await sample.ManagementClient.Vaults.GetDeletedAsync(vaultName, retrievedVault.Location).ConfigureAwait(false);
Console.WriteLine("done; '{0}' deleted on: {1}, scheduled for purge on: {2}", deletedVault.Id, deletedVault.Properties.DeletionDate, deletedVault.Properties.ScheduledPurgeDate);

Expand Down
39 changes: 14 additions & 25 deletions KeyVaultSampleBase.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using System;
using Azure;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Azure.Management.KeyVault.Fluent;
using Microsoft.Azure.Management.KeyVault.Fluent.Models;
using Microsoft.Rest;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.KeyVault.Models;
using Microsoft.Azure.Management.KeyVault.Fluent;
using Microsoft.Azure.Management.KeyVault.Fluent.Models;
using Microsoft.Rest;

namespace AzureKeyVaultRecoverySamples
{
Expand All @@ -30,7 +31,10 @@ public class KeyVaultSampleBase
/// <summary>
/// KeyVault data (Data Plane) client instance.
/// </summary>
public KeyVaultClient DataClient { get; private set; }
public SecretClient GetDataClient(Uri vaultUri)
{
return new SecretClient(vaultUri, new DefaultAzureCredential());
}

/// <summary>
/// Builds a sample object from the specified parameters.
Expand Down Expand Up @@ -75,10 +79,7 @@ private void InstantiateSample(string tenantId, string objectId, string appId, s

// instantiate the management client
ManagementClient = new KeyVaultManagementClient(serviceCredentials);
ManagementClient.SubscriptionId = subscriptionId;

// instantiate the data client
DataClient = new KeyVaultClient(ClientContext.AcquireTokenAsync);
ManagementClient.SubscriptionId = subscriptionId;
}

#region utilities
Expand Down Expand Up @@ -139,23 +140,11 @@ public async Task EnableRecoveryOptionsOnExistingVaultAsync(string resourceGroup
if (vault.Properties.EnableSoftDelete.HasValue
&& vault.Properties.EnableSoftDelete.Value)
{
//if (!(vault.Properties.EnablePurgeProtection ^ enablePurgeProtection))
//{
Console.WriteLine("The required recovery protection level is already enabled on vault {0}.", vaultName);

return;
//}

// check if this is an attempt to lower the recovery level.
//if (vault.Properties.EnablePurgeProtection
// && !enablePurgeProtection)
//{
// throw new InvalidOperationException("The recovery level on an existing vault cannot be lowered.");
//}
}

vault.Properties.EnableSoftDelete = true;
//vault.Properties.EnablePurgeProtection = enablePurgeProtection;

// prepare the update operation on the vault
var updateParameters = new VaultCreateOrUpdateParametersInner
Expand Down Expand Up @@ -228,9 +217,9 @@ public async static Task<HttpOperationResponse> RetryHttpRequestAsync(

break;
}
catch (KeyVaultErrorException kvee)
catch (RequestFailedException kvee)
{
var statusCode = kvee.Response.StatusCode;
HttpStatusCode statusCode = (HttpStatusCode)kvee.Status;

Console.Write("attempt #{0} to {1} returned: {2};", idx, functionName, statusCode);
if (continueOn.Contains(statusCode))
Expand Down
Loading