diff --git a/src/KeyVault/KeyVault.Test/PesterTests/ManagedHsmDatePlaneTests.Tests.ps1 b/src/KeyVault/KeyVault.Test/PesterTests/ManagedHsmDatePlaneTests.Tests.ps1
index a6d1fbe7dbd3..11e841f4364b 100644
--- a/src/KeyVault/KeyVault.Test/PesterTests/ManagedHsmDatePlaneTests.Tests.ps1
+++ b/src/KeyVault/KeyVault.Test/PesterTests/ManagedHsmDatePlaneTests.Tests.ps1
@@ -4,11 +4,12 @@ $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. $PSScriptRoot/ManagedHsmDatePlaneTests.ps1
# ImportModules
-$hsmName = 'yeminghsm02'
+$hsmName = 'bezmhsm'
$signInName = 'yeliu@microsoft.com'
-$storageAccount = 'yemingsa01'
-$containerName = 'hsmbackup'
-$sasToken = ConvertTo-SecureString -AsPlainText -Force 'insert sas token'
+$storageAccount = 'bezstorageaccount'
+$containerName = 'backup'
+$keyName = 'test'
+# $sasToken = ConvertTo-SecureString -AsPlainText -Force 'insert sas token'
$certs = "D:\sd1.cer", "D:\sd2.cer", "D:\sd3.cer" # for security domain
$certsKeys = @{PublicKey = "D:\sd1.cer"; PrivateKey = "D:\sd1.key" }, @{PublicKey = "D:\sd2.cer"; PrivateKey = "D:\sd2.key" }, @{PublicKey = "D:\sd3.cer"; PrivateKey = "D:\sd3.key" }
@@ -170,15 +171,24 @@ Describe "BackupAndRestoreAzManagedHsmKey" {
Describe "BackupAndRestoreAzManagedHsm" {
$script:backupUri = ''
$containerUri = "https://$storageAccount.blob.core.windows.net/$containerName"
-
It "Backup then restore a managed HSM" {
$script:backupUri = Backup-AzKeyVault -HsmName $hsmName -StorageContainerUri $containerUri -SasToken $sasToken
$script:backupUri | Should -Not -Be $null
}
+ It "Selective restore a managed HSM"{
+ $script:backupUri = [System.Uri]::new($script:backupUri)
+ $backupFolder = $script:backupUri.Segments[$script:backupUri.Segments.Length - 1]
+ $restoreResult = Restore-AzKeyVault -HsmName $hsmName -KeyName $keyName -StorageContainerUri $containerUri -BackupFolder $backupFolder -SasToken $sasToken -PassThru
+ $restoreResult | Should -Be $True
+ }
+
It "Restore a managed HSM" {
$script:backupUri = [System.Uri]::new($script:backupUri)
$backupFolder = $script:backupUri.Segments[$script:backupUri.Segments.Length - 1]
+ # Clean hsm
+ Get-AzKeyVaultKey -HsmName $hsmName | Remove-AzKeyVaultKey -Force
+ Get-AzKeyVaultKey -HsmName $hsmName -InRemovedState| Remove-AzKeyVaultKey -InRemovedState -Force
$restoreResult = Restore-AzKeyVault -HsmName $hsmName -StorageContainerUri $containerUri -BackupFolder $backupFolder -SasToken $sasToken -PassThru
$restoreResult | Should -Be $True
}
diff --git a/src/KeyVault/KeyVault/ChangeLog.md b/src/KeyVault/KeyVault/ChangeLog.md
index 2c9fb7f6af66..077c604c9722 100644
--- a/src/KeyVault/KeyVault/ChangeLog.md
+++ b/src/KeyVault/KeyVault/ChangeLog.md
@@ -18,6 +18,7 @@
- Additional information about change #1
-->
## Upcoming Release
+* Supported selective restore a key from a managed HSM full backup [#13526]
* Added missing return objects of `Get-Secret` in SecretManagement module
## Version 3.2.0
diff --git a/src/KeyVault/KeyVault/Commands/FullBackupRestore/FullBackupRestoreCmdletBase.cs b/src/KeyVault/KeyVault/Commands/FullBackupRestore/FullBackupRestoreCmdletBase.cs
index 3e40a2dfe1fb..a764e5dab403 100644
--- a/src/KeyVault/KeyVault/Commands/FullBackupRestore/FullBackupRestoreCmdletBase.cs
+++ b/src/KeyVault/KeyVault/Commands/FullBackupRestore/FullBackupRestoreCmdletBase.cs
@@ -42,8 +42,8 @@ public abstract class FullBackupRestoreCmdletBase : KeyVaultCmdletBase
[Parameter(Mandatory = true, HelpMessage = "The shared access signature (SAS) token to authenticate the storage account.")]
public SecureString SasToken { get; set; }
- [Parameter(ParameterSetName = InputObjectStorageUri, Mandatory = true, HelpMessage = "Managed HSM object")]
- [Parameter(ParameterSetName = InputObjectStorageName, Mandatory = true, HelpMessage = "Managed HSM object")]
+ [Parameter(ParameterSetName = InputObjectStorageUri, Mandatory = true, ValueFromPipeline = true, HelpMessage = "Managed HSM object")]
+ [Parameter(ParameterSetName = InputObjectStorageName, Mandatory = true, ValueFromPipeline = true, HelpMessage = "Managed HSM object")]
public PSManagedHsm HsmObject { get; set; }
public override void ExecuteCmdlet()
diff --git a/src/KeyVault/KeyVault/Commands/FullBackupRestore/RestoreAzureManagedHsm.cs b/src/KeyVault/KeyVault/Commands/FullBackupRestore/RestoreAzureManagedHsm.cs
index 5d06d3068aa0..e5aa534d5d4e 100644
--- a/src/KeyVault/KeyVault/Commands/FullBackupRestore/RestoreAzureManagedHsm.cs
+++ b/src/KeyVault/KeyVault/Commands/FullBackupRestore/RestoreAzureManagedHsm.cs
@@ -14,6 +14,9 @@ public class RestoreAzureManagedHsm : FullBackupRestoreCmdletBase
[Parameter(Mandatory = true, HelpMessage = "Folder name of the backup, e.g. 'mhsm-*-2020101309020403'.\nIt can also be nested such as 'backups/mhsm-*-2020101309020403'.")]
public string BackupFolder { get; set; }
+ [Parameter(Mandatory = false, HelpMessage = "Key name to restore.")]
+ public string KeyName { get; set; }
+
[Parameter(Mandatory = false, HelpMessage = "Return true when the HSM is restored.")]
public SwitchParameter PassThru { get; set; }
@@ -23,14 +26,24 @@ public override void DoExecuteCmdlet()
string.Format(Resources.DoFullRestore, StorageContainerUri),
HsmName, () =>
{
+ var errorMsg = string.Format(Resources.FullRestoreFailed, HsmName);
try
{
- Track2DataClient.RestoreHsm(HsmName, StorageContainerUri, SasToken.ConvertToString(), BackupFolder);
+ if(KeyName == null)
+ {
+ Track2DataClient.RestoreHsm(HsmName, StorageContainerUri, SasToken.ConvertToString(), BackupFolder);
+ }
+ else
+ {
+ Track2DataClient.SelectiveRestoreHsm(HsmName, KeyName,StorageContainerUri, SasToken.ConvertToString(), BackupFolder);
+ errorMsg = string.Format(Resources.SelectiveRestoreFailed, KeyName, HsmName);
+ }
}
catch (Exception ex)
{
- throw new Exception(string.Format(Resources.FullRestoreFailed, HsmName), ex);
+ throw new Exception(errorMsg, ex);
}
+
if (PassThru)
{
WriteObject(true);
diff --git a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs
index 3a2867f86614..0657f379be84 100644
--- a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs
+++ b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs
@@ -25,7 +25,9 @@ namespace Microsoft.Azure.Commands.KeyVault.Models
{
public interface IKeyVaultDataServiceClient
{
+ #region Key actions
PSKeyVaultKey CreateKey(string vaultName, string keyName, PSKeyVaultKeyAttributes keyAttributes, int? size, string curveName);
+
PSKeyVaultKey CreateManagedHsmKey(string managedHsmName, string keyName, PSKeyVaultKeyAttributes keyAttributes, int? size, string curveName);
PSKeyVaultKey ImportKey(string vaultName, string keyName, PSKeyVaultKeyAttributes keyAttributes, JsonWebKey webKey, bool? importToHsm);
@@ -68,6 +70,16 @@ public interface IKeyVaultDataServiceClient
PSKeyVaultKey RecoverManagedHsmKey(string managedHsmName, string keyName);
+ string BackupKey(string vaultName, string keyName, string outputBlobPath);
+
+ string BackupManagedHsmKey(string managedHsmName, string keyName, string outputBlobPath);
+
+ PSKeyVaultKey RestoreKey(string vaultName, string inputBlobPath);
+
+ PSKeyVaultKey RestoreManagedHsmKey(string managedHsmName, string inputBlobPath);
+ #endregion
+
+ #region Secret actions
PSKeyVaultSecret SetSecret(string vaultName, string secretName, SecureString secretValue, PSKeyVaultSecretAttributes secretAttributes);
PSKeyVaultSecret UpdateSecret(string vaultName, string secretName, string secretVersion, PSKeyVaultSecretAttributes secretAttributes);
@@ -88,17 +100,10 @@ public interface IKeyVaultDataServiceClient
PSKeyVaultSecret RecoverSecret(string vaultName, string secretName);
- string BackupKey(string vaultName, string keyName, string outputBlobPath);
-
- string BackupManagedHsmKey(string managedHsmName, string keyName, string outputBlobPath);
-
- PSKeyVaultKey RestoreKey(string vaultName, string inputBlobPath);
-
- PSKeyVaultKey RestoreManagedHsmKey(string managedHsmName, string inputBlobPath);
-
string BackupSecret(string vaultName, string secretName, string outputBlobPath);
PSKeyVaultSecret RestoreSecret(string vaultName, string inputBlobPath);
+ #endregion
#region Certificate actions
@@ -198,7 +203,10 @@ public interface IKeyVaultDataServiceClient
#region Full backup restore
Uri BackupHsm(string hsmName, Uri blobStorageUri, string sasToken);
+
void RestoreHsm(string hsmName, Uri backupLocation, string sasToken, string backupFolder);
+
+ void SelectiveRestoreHsm(string hsmName, string keyName, Uri backupLocation, string sasToken, string backupFolder);
#endregion
#region RBAC
diff --git a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs
index 1403075323d0..0f6f7b5fe801 100644
--- a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs
+++ b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs
@@ -2019,6 +2019,7 @@ public PSKeyVaultKey CreateManagedHsmKey(string managedHsmName, string keyName,
throw new NotImplementedException("Creating keys on managed HSM is only possible in track 2 SDK.");
}
+ #region Full backup restore
public Uri BackupHsm(string hsmName, Uri blobStorageUri, string sasToken)
{
throw new NotImplementedException();
@@ -2029,6 +2030,12 @@ public void RestoreHsm(string hsmName, Uri blobStorageUri, string sasToken, stri
throw new NotImplementedException();
}
+ public void SelectiveRestoreHsm(string hsmName, string keyName, Uri backupLocation, string sasToken, string backupFolder)
+ {
+ throw new NotImplementedException();
+ }
+ #endregion
+
public PSKeyVaultRoleDefinition[] GetHsmRoleDefinitions(string name, string scope)
{
throw new NotImplementedException();
@@ -2103,6 +2110,8 @@ public void PurgeManagedHsmKey(string managedHsmName, string keyName)
throw new NotImplementedException("Purging deleted keys on managed HSM is only possible in track 2 SDK.");
}
+
+
private VaultUriHelper vaultUriHelper;
private KeyVaultClient keyVaultClient;
}
diff --git a/src/KeyVault/KeyVault/Properties/Resources.Designer.cs b/src/KeyVault/KeyVault/Properties/Resources.Designer.cs
index 7e3d64c0359f..2e2e5ebed8c8 100644
--- a/src/KeyVault/KeyVault/Properties/Resources.Designer.cs
+++ b/src/KeyVault/KeyVault/Properties/Resources.Designer.cs
@@ -1260,6 +1260,15 @@ internal static string RoleDefinitionNotFound {
}
}
+ ///
+ /// Looks up a localized string similar to Failed to selective restore key {0} of managed HSM {1}..
+ ///
+ internal static string SelectiveRestoreFailed {
+ get {
+ return ResourceManager.GetString("SelectiveRestoreFailed", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Set certificate attribute.
///
diff --git a/src/KeyVault/KeyVault/Properties/Resources.resx b/src/KeyVault/KeyVault/Properties/Resources.resx
index 3534b9652e3d..15f9cf62b0a5 100644
--- a/src/KeyVault/KeyVault/Properties/Resources.resx
+++ b/src/KeyVault/KeyVault/Properties/Resources.resx
@@ -582,4 +582,7 @@ You can find the object ID using Azure Active Directory Module for Windows Power
Updating managed HSM '{0}' in resource group '{1}'.
+
+ Failed to selective restore key {0} of managed HSM {1}.
+
\ No newline at end of file
diff --git a/src/KeyVault/KeyVault/Track2Models/Track2HsmClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2HsmClient.cs
index 79605ff04179..62524976ca51 100644
--- a/src/KeyVault/KeyVault/Track2Models/Track2HsmClient.cs
+++ b/src/KeyVault/KeyVault/Track2Models/Track2HsmClient.cs
@@ -187,6 +187,20 @@ public void RestoreHsm(string hsmName, Uri backupLocation, string sasToken, stri
throw;
}
}
+
+ public void SelectiveRestoreHsm(string hsmName, string keyName, Uri backupLocation, string sasToken, string backupFolder)
+ {
+ var client = CreateBackupClient(hsmName);
+ try
+ {
+ client.StartSelectiveRestore(keyName, backupLocation, sasToken, backupFolder)
+ .WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult();
+ }
+ catch
+ {
+ throw;
+ }
+ }
public PSKeyVaultRoleDefinition[] GetHsmRoleDefinitions(string hsmName, string scope)
{
diff --git a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs
index 172cdac59d81..8fd0f8284166 100644
--- a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs
+++ b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs
@@ -388,6 +388,7 @@ public PSKeyVaultSecret UpdateSecret(string vaultName, string secretName, string
throw new NotImplementedException();
}
+ #region Full backup restore
public Uri BackupHsm(string hsmName, Uri blobStorageUri, string sasToken)
{
return HsmClient.BackupHsm(hsmName, blobStorageUri, sasToken);
@@ -398,6 +399,12 @@ public void RestoreHsm(string hsmName, Uri backupLocation, string sasToken, stri
HsmClient.RestoreHsm(hsmName, backupLocation, sasToken, backupFolder);
}
+ public void SelectiveRestoreHsm(string hsmName, string keyName, Uri backupLocation, string sasToken, string backupFolder)
+ {
+ HsmClient.SelectiveRestoreHsm(hsmName, keyName, backupLocation, sasToken, backupFolder);
+ }
+ #endregion
+
public PSKeyVaultRoleDefinition[] GetHsmRoleDefinitions(string hsmName, string scope)
{
return HsmClient.GetHsmRoleDefinitions(hsmName, scope);
diff --git a/src/KeyVault/KeyVault/help/Restore-AzKeyVault.md b/src/KeyVault/KeyVault/help/Restore-AzKeyVault.md
index 1328aa76540f..472a8f8ae2b0 100644
--- a/src/KeyVault/KeyVault/help/Restore-AzKeyVault.md
+++ b/src/KeyVault/KeyVault/help/Restore-AzKeyVault.md
@@ -14,26 +14,28 @@ Fully restores a managed HSM from backup.
### InteractiveStorageName (Default)
```
-Restore-AzKeyVault -BackupFolder [-PassThru] [-HsmName] -StorageAccountName
- -StorageContainerName -SasToken [-DefaultProfile ] [-WhatIf]
- [-Confirm] []
+Restore-AzKeyVault -BackupFolder [-KeyName ] [-PassThru] [-HsmName]
+ -StorageAccountName -StorageContainerName -SasToken
+ [-DefaultProfile ] [-WhatIf] [-Confirm] []
```
### InteractiveStorageUri
```
-Restore-AzKeyVault -BackupFolder [-PassThru] [-HsmName] -StorageContainerUri
- -SasToken [-DefaultProfile ] [-WhatIf] [-Confirm] []
+Restore-AzKeyVault -BackupFolder [-KeyName ] [-PassThru] [-HsmName]
+ -StorageContainerUri -SasToken [-DefaultProfile ] [-WhatIf]
+ [-Confirm] []
```
### InputObjectStorageUri
```
-Restore-AzKeyVault -BackupFolder [-PassThru] -StorageContainerUri -SasToken
- -HsmObject [-DefaultProfile ] [-WhatIf] [-Confirm] []
+Restore-AzKeyVault -BackupFolder [-KeyName ] [-PassThru] -StorageContainerUri
+ -SasToken -HsmObject [-DefaultProfile ] [-WhatIf]
+ [-Confirm] []
```
### InputObjectStorageName
```
-Restore-AzKeyVault -BackupFolder [-PassThru] -StorageAccountName
+Restore-AzKeyVault -BackupFolder [-KeyName ] [-PassThru] -StorageAccountName
-StorageContainerName -SasToken -HsmObject
[-DefaultProfile ] [-WhatIf] [-Confirm] []
```
@@ -112,6 +114,21 @@ Aliases:
Required: True
Position: Named
Default value: None
+Accept pipeline input: True (ByValue)
+Accept wildcard characters: False
+```
+
+### -KeyName
+Key name to restore.
+
+```yaml
+Type: System.String
+Parameter Sets: (All)
+Aliases:
+
+Required: False
+Position: Named
+Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```