Azure / Azure/azure-powershell
Import-AzureKeyVaultCertificate does not work when attempting to move a pfx cert from one keyvault to another
- Dominant language
- C#
- Stars
- 4.8k
- Forks
- 4.3k
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 51
Description
## Description
CONTEXT: We are automating our pipeline to enable SSL binding for a webapp using custom hostnames. To do this, we need to import a certificate from an already existing shared vault. Ideally, I would be able to do this in memory as opposed to downloading the file onto the agent.
PROBLEM: When attempting to import this certificate using the documentation provided, I am getting the below errors for the scenarios outlined below. It appears this function does not support importing a certificate from another keyvault, even though the documentation indicates this can be done. Can you please provide an example, as they are not provided in the documentation to clearly articulate the use of a CertificateCollection and a CertificateString.
## Steps to reproduce
```
1.) Set up a shared keyvault with a pfx cert
2.) Add a secret to that shared vault using the naming convention: -password
3.) Set up a webapp
4.) Set up a custom hostname for which you wish to bind the cert
5.) run the script below passing in the proper parameters locally
```
```
<#
.SYNOPSIS
Enables the SSL state for a webapp that uses custom hostnames.
.DESCRIPTION
When provided a webapp name (the name of app which SSL is to be enabled), a prexisting keyvault name (where the SSL cert (.pfx) is stored) and a prexisting certificate name, the SSL state will become 'sniEnabled', and the SSL certificate will be bound to the webapp.
.PARAMETER WebappName
Specifies the name of the webapp resource for which SSL is to be enabled.
Default SSL state will be: 'sniEnabled'
.PARAMETER existingKeyvaultName
Specifies the name of the pre-existing shared keyvault containing the .pfx certificate that will be bind to your webapp.
.PARAMETER existingCertificateName
Specifies the name of the pre-existing certificate in the specified keyvault that will be bound to the webapp.
.INPUTS
This function does not support any piped inputs
.OUTPUTS
Returns status of the request to the keyvault, the request for the secret provided, and the status of the binding to the hostname.
.EXAMPLE
PS>
PS>
.LINK
http://linktoWiki
#>
function Enable-SslStateOnWebapp {
[CmdletBinding()]
param (
[Parameter(
Mandatory = $true,
HelpMessage = 'A webapp name is required.')]
[ValidateNotNullOrEmpty()]
[string] $WebappName,
[PARAMETER(
Mandatory = $true,
HelpMessage = 'The FQDN of the custom hostname you want to bind.')]
[ValidateNotNullOrEmpty()]
[string] $customWebappHostname,
[Parameter(
Mandatory = $true,
HelpMessage = 'A name for an existing Keyvault is required.')]
[ValidateNotNullOrEmpty()]
[string] $existingKeyvaultName,
[PARAMETER(
Mandatory = $true,
HelpMessage = 'A name of the pfx certificate stored in the pre-existing keyvault')]
[ValidateNotNullOrEmpty()]
[string] $existingKeyVaultCertName
)
#getting webapp resources
$webapp = Get-AzureRmResource -Name $webappName
#obtaining resource group resources through the use of resource group name tied to webapp
$rg = Get-AzureRmResource -ResourceGroupName $webapp.ResourceGroupName
#get cert from existing keyvault using provided parameters
$cert = Get-AzureKeyVaultSecret -VaultName $existingKeyvaultName -Name $existingKeyVaultCertName
$certFull = Get-AzureKeyVaultCertificate -VaultName $existingKeyvaultName -Name $existingKeyVaultCertName
if ($cert -eq $null) {
Throw "Unable to get the certificate: '$($existingKeyVaultCertName)' from the keyvault: '$($existingKeyvaultName)'. Please check that you have provided the correct vault and certificate name and try again."
}
#get cert password from existing keyvault using provided parameters
$password = Get-AzureKeyVaultSecret -VaultName $existingKeyVaultName -Name "$($existingKeyVaultCertName)-password"
if (-not $password) {
Throw "Unable to get the secret: '$($existingKeyVaultCertName)-password' from the keyvault: '$($existingKeyvaultName)'. Please check that you have provided the correct vault and password name and try again. Note: a secret stored in a keyvault that contains the password for a PFX certificate must be named in the following pattern '-password'."
}
if ($rg) {
#iterating over the resources and pulling those matching the appropriate resource type
$kv = $rg | Where-Object { $_.ResourceType -Match 'Microsoft.KeyVault/vaults' }
#iterating through the keyvaults and their access policies, looking for the keyvault that contains an access policy for the webapp.
$vaults = $kv | ForEach-Object {Get-AzureRmKeyVault -Name $_.Name}
$vaults | ForEach-Object {$_.accessPolicies } | ForEach-Object {
$a = $_
if($_.ObjectId -eq $webapp.Identity.PrincipalId){
Write-Host "Found the keyvault $($a.DisplayName)." -Verbose
$newKeyVaultName = $a.DisplayName.split(" ")[0]
}
}
#import the cert into a collection in memory
$certBytes = [System.Convert]::FromBase64String($cert.SecretValueText)
$certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$certCollection.Import($certBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
#export the cert collection to the agent working directory
$protectedCertificateBytes = $certCollection.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12, $password.SecretText)
$certPath = "$(System.DefaultWorkingDirectory)/SslCert.pfx"
[System.IO.File]::WriteAllBytes($certPath, $protectedCertificateBytes)
#move the cert to a new keyvault as a certcollection
Import-AzureKeyVaultCertificate -VaultName $newKeyVaultName -Name $existingKeyVaultCertName -FilePath $certPath -password $password.SecretValue
#add the ssl binding to the webapp using the cert
New-AzureRmWebAppSSLBinding -ResourceGroupName $webApp.ResourceGroupName -WebAppName $webappName -Thumbprint $certFull.Thumbprint -Name $customWebappHostname -SslState "SniEnabled"
}
else {
Throw "Unable to obtain the resource group for the web application $($webappName). Please check the name any try again."
}
}
```
## Module versions
```
PS> (Get-Module -Name AzureRM* -ListAvailable)
Directory: /usr/local/share/powershell/Modules
ModuleType Version Name PSEdition ExportedCom
mands
---------- ------- ---- --------- -----------
Script 6.13.1 AzureRM Desk
Script 0.6.14 AzureRM.AnalysisServices Desk {Resume-...
Script 6.1.7 AzureRM.ApiManagement Desk {Add-Azu...
Script 0.1.8 AzureRM.ApplicationInsights Desk {Get-Azu...
Script 6.1.1 AzureRM.Automation Desk {Get-Azu...
Script 4.0.11 AzureRM.Backup Desk {Backup-...
Script 4.1.5 AzureRM.Batch Desk {Remove-...
Script 0.14.6 AzureRM.Billing Desk {Get-Azu...
Script 5.0.6 AzureRM.Cdn Desk {Get-Azu...
Script 0.9.12 AzureRM.CognitiveServices Desk {Get-Azu...
Script 5.9.1 AzureRM.Compute Desk {Remove-...
Script 0.3.7 AzureRM.Consumption Desk {Get-Azu...
Script 0.2.12 AzureRM.ContainerInstance Desk {New-Azu...
Script 1.0.10 AzureRM.ContainerRegistry Desk {New-Azu...
Script 5.0.3 AzureRM.DataFactories Desk {Remove-...
Script 0.5.11 AzureRM.DataFactoryV2 Desk {Set-Azu...
Script 5.1.4 AzureRM.DataLakeAnalytics Desk {Get-Azu...
Script 6.2.1 AzureRM.DataLakeStore Desk {Get-Azu...
Script 4.0.9 AzureRM.DevTestLabs Desk {Get-Azu...
Script 5.1.0 AzureRM.Dns Desk {Get-Azu...
Script 0.3.7 AzureRM.EventGrid Desk {New-Azu...
Script 0.7.0 AzureRM.EventHub Desk {New-Azu...
Script 4.1.8 AzureRM.HDInsight Desk {Get-Azu...
Script 5.1.5 AzureRM.Insights Desk {Get-Azu...
Script 3.1.8 AzureRM.IotHub Desk {Add-Azu...
Script 5.2.1 AzureRM.KeyVault Desk {Add-Azu...
Script 4.1.4 AzureRM.LogicApp Desk {Get-Azu...
Script 0.18.5 AzureRM.MachineLearning Desk {Move-Az...
Script 0.4.8 AzureRM.MachineLearningCompute Desk {Get-Azu...
Script 0.2.7 AzureRM.MarketplaceOrdering Desk {Get-Azu...
Script 0.10.4 AzureRM.Media Desk {Sync-Az...
Script 6.11.1 AzureRM.Network Desk {Add-Azu...
Script 5.0.3 AzureRM.NotificationHubs Desk {Get-Azu...
Script 5.0.6 AzureRM.OperationalInsights Desk {New-Azu...
Script 1.1.0 AzureRM.PolicyInsights Desk {Get-Azu...
Script 4.1.10 AzureRM.PowerBIEmbedded Desk {Remove-...
Script 5.8.2 AzureRM.profile Desk {Disable...
Script 4.1.9 AzureRM.RecoveryServices Desk {Get-Azu...
Script 4.5.2 AzureRM.RecoveryServices.Backup Desk {Backup-...
Script 0.2.12 AzureRM.RecoveryServices.SiteRec... Desk {Edit-Az...
Script 5.1.0 AzureRM.RedisCache Desk {Remove-...
Script 0.3.12 AzureRM.Relay Desk {New-Azu...
Script 6.7.3 AzureRM.Resources Desk {Get-Azu...
Script 0.16.10 AzureRM.Scheduler Desk {Disable...
Script 0.6.13 AzureRM.ServiceBus Desk {New-Azu...
Script 0.3.15 AzureRM.ServiceFabric Desk {Add-Azu...
Script 1.0.0 AzureRM.SignalR Desk {New-Azu...
Script 4.12.1 AzureRM.Sql Desk {Get-Azu...
Script 5.2.0 AzureRM.Storage Desk {Get-Azu...
Script 4.0.10 AzureRM.StreamAnalytics Desk {Get-Azu...
Script 4.0.5 AzureRM.Tags Desk {Remove-...
Script 4.1.3 AzureRM.TrafficManager Desk {Add-Azu...
Script 4.0.5 AzureRM.UsageAggregates Desk Get-Usag...
Script 5.2.0 AzureRM.Websites Desk {Get-Azu...
```
## Error output
```
When attempting to import a cert using from another keyvault using a CertificateCollection:
Import-AzureKeyVaultCertificate -VaultName $newKeyVaultName -Name $existingKeyVaultCertName -CertificateCollection $certCollection
Import-AzureKeyVaultCertificate : Device not configured
At /grs-arm/enable-ssl_state_on_webapp/enable-ssl_state_on_webapp.ps1:100 char:9
+ Import-AzureKeyVaultCertificate -VaultName $newKeyVaultName - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Import-AzKeyVaultCertificate], SocketException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.KeyVault.ImportAzureKeyVaultCertificate
When attempting to import a cert using a secure string and password:
Import-AzureKeyVaultCertificate -VaultName $newKeyVaultName -Name $existingKeyVaultCertName -CertificateString $certCollection -password $password.SecretText
Import-AzureKeyVaultCertificate : Device not configured
At /grs-arm/enable-ssl_state_on_webapp/enable-ssl_state_on_webapp.ps1:100 char:9
+ Import-AzureKeyVaultCertificate -VaultName $newKeyVaultName - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Import-AzKeyVaultCertificate], SocketException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.KeyVault.ImportAzureKeyVaultCertificate
```
Contributor guide
Assessment
This issue has not been assessed yet.