I'm building a set of scripts and templates to create a Service Fabric cluster in Azure. I've got a script that creates a key vault and a self-signed certificate and successfully uploads it to the vault. Another script creates the cluster but it's hitting an error at the point that the certs are linked to the VMs. The error from the New-AzureRmResourceGroupDeployment command is:-
{
"status": "Failed",
"error": {
"code": "ResourceDeploymentFailure",
"message": "The resource operation completed with terminal provisioning state 'Failed'.",
"details": [
{
"code": "KeyVaultAccessForbidden",
"message": "Key Vault https://VAULT-NAME.vault.azure.net/secrets/clusterCert/SECRET-ID either has not been enabled for deployment or the vault id provided, /subscriptions/SUBSCRIPTION-ID/resourceGroups/jg-sf/providers/Microsoft.KeyVault/vaults/VAULTNAME, does not match the Key Vault's true resource id."
}
]
}
}
VAULT-NAME, SUBSCRIPTION-ID and SECRET-ID are all correct. The key vault has been created with the parameter "enabledForTemplateDeployment": true, as evidenced in the following screenshot.
My scripts and templates can be seen in GitHub - https://github.com/goochjs/azure-testbed.
How do I diagnose the issue?
Thanks,
Jeremy.
How do you create the key vault, I use the following script to create key vault and get CertificateURL.
New-AzureRmKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroup -Location $Location -sku standard -EnabledForDeployment
#Creates a new selfsigned cert and exports a pfx cert to a directory on disk
$NewCert = New-SelfSignedCertificate -CertStoreLocation Cert:\CurrentUser\My -DnsName $CertDNSName
Export-PfxCertificate -FilePath $CertFileFullPath -Password $SecurePassword -Cert $NewCert
Import-PfxCertificate -FilePath $CertFileFullPath -Password $SecurePassword -CertStoreLocation Cert:\LocalMachine\My
#Reads the content of the certificate and converts it into a json format
$Bytes = [System.IO.File]::ReadAllBytes($CertFileFullPath)
$Base64 = [System.Convert]::ToBase64String($Bytes)
$JSONBlob = #{
data = $Base64
dataType = 'pfx'
password = $Password
} | ConvertTo-Json
$ContentBytes = [System.Text.Encoding]::UTF8.GetBytes($JSONBlob)
$Content = [System.Convert]::ToBase64String($ContentBytes)
#Converts the json content a secure string
$SecretValue = ConvertTo-SecureString -String $Content -AsPlainText -Force
#Creates a new secret in Azure Key Vault
$NewSecret = Set-AzureKeyVaultSecret -VaultName $KeyVaultName -Name $KeyVaultSecretName -SecretValue $SecretValue -Verbose
#Writes out the information you need for creating a secure cluster
Write-Host
Write-Host "Resource Id: "$(Get-AzureRmKeyVault -VaultName $KeyVaultName).ResourceId
Write-Host "Secret URL : "$NewSecret.Id
Write-Host "Thumbprint : "$NewCert.Thumbprint
More information about this, please refer to this blog.
I suggest you could check your Resource Id format. The correct format is like /subscriptions/***************/resourceGroups/westus-mykeyvault/providers/Microsoft.KeyVault/vaults/shuisfsvault. You could create SF cluster on Azure Portal firstly.
If it still does not work, I suggest you could check your key vault, do you give enough permission to it?
Note: For test, you could give all permission to the user.
Related
I have a powershell script that is attempting to list all the expired secrets of my Azure Key Vault. Unfortunately I'm struggling to do this.
This is how I retrieve sercrets. But what do I need to add to get the expiration of all secrets? Then delete those that are expired? I'm guessing I'll need to set an access policy.
Select-AzSubscription -Subscription "My subscriptsion"
Set-AzKeyVaultAccessPolicy -VaultName "testKeyVaultPwsh" -UserPrincipalName "mystuff#domain.com" -PermissionsToSecrets get,set,delete
#Retrieve secret
$secret = Get-AzKeyVaultSecret -VaultName "testKeyVaultPwsh" -Name "ExamplePassword" -AsPlainText
You can delete the expired secrets using below commands .(Make sure
you have get,set,delete access policies set and given proper
permissions )
I have tried in my environment and able to delete expired secrets sussessfully.
After checking expiry using
$exp =Get-AzKeyVaultSecret -VaultName $vaultname -Name $secretname | Select-Object Name,Expires
$exp
I created secrets and have secrets expired.
Commands:
$vaultname= “<keyvaultname>”
$secrets= Get-AzKeyVaultSecret -VaultName $vaultname
$secretnames =$secrets.Name
$current_date=Get-Date
Foreach($secretname in $secretnames)
{
$exp =Get-AzKeyVaultSecret -VaultName $vaultname -Name $secretname | Select-Object Expires
$keyvaultsecretvexpirydate =[datetime]($exp.Expires)
$timediff=NEW-TIMESPAN -Start $current_date -End $keyvaultsecretvexpirydate
$days_until_expiration=$timediff.Days
Write-Output “days_until_expiration of secret named $secretname is :$days_until_expiration”
Write-Output “ ”
if ($days_until_expiration -eq 0)
{
Write-Output "Secret named $secretname got expired “
Write-Output “removing expired secret : $secretname”
Write-Output “ ”
Remove-AzKeyVaultSecret -VaultName $vaultname -Name $secretname
}
}
Confirm to delete by typing Y and refresh the secrets page to see the expired secret being removed/deleted.
References:
KeyVaultSecretExpirationAlerts |github
remove-azkeyvaultsecret | microsoftdocs
I am trying to check if an azure key vault already exists in a resource group using PowerShell. If the vault with the same name already exists even in the deleted state I only want to receive a user friendly message saying Key Vault already exists or catch the exception if there is any. I don't want the terminal to blow up with errors. If the key vault does not exist I want to create a new keyvault.
I have the following code:
$KeyVaultName = "Key Vault Name"
$ResourceGroupName = "Resource group name"
$KeyVault = Get-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -ErrorAction SilentlyContinue
if($null -eq $KeyVault){
New-AzKeyVault -ResourceGroupName $ResourceGroupName -VaultName $KeyVaultName -Location "Switzerland North"
}
else{
Write-Host "$KeyVaultName already exists"
}
After executing the code I am getting this error message on the terminal:
New-AzKeyVault : A vault with the same name already exists in deleted state. You need to either recover or purge existing key vault.
I also tried using the following code as well:
if (!(Test-AzureName -Service $KeyVaultName))
{
New-AzKeyVault -ResourceGroupName $ResourceGroupName -VaultName $KeyVaultName -Location "Switzerland North"
}
It gives me the following error after execution:
Test-AzureName : No default subscription has been designated. Use Select-AzureSubscription -Default to set the default subscription.
Though I only have one subscription being used.
Can someone please tell me if I am doing something wrong here ? Can you please provide me with an efficient way to achieve this ?
You can try something like the following:
$KeyVaultName = "keyvaultname"
$ResourceGroupName = "resourcegroupname"
$KeyVaultLocation = "keyvaultlocation"
$KeyVault = Get-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -ErrorAction SilentlyContinue
if($null -eq $KeyVault){
$KeyVault = Get-AzKeyVault -VaultName $KeyVaultName -Location $KeyVaultLocation -InRemovedState -ErrorAction SilentlyContinue
if ($null -eq $KeyVault) {
New-AzKeyVault -ResourceGroupName $ResourceGroupName -VaultName $KeyVaultName -Location $KeyVaultLocation
} else {
Write-Host "$KeyVaultName exists but is in soft-deleted state"
}
}
else{
Write-Host "$KeyVaultName already exists"
}
Essentially what we are doing here is first checking if the Key Vault exists and is in active state. If we do not find any Key Vault, then we are checking if the Key Vault is in soft deleted state. If no results are found, then we are proceeding with creation of new Key Vault.
However, please note that Key Vault name is globally unique so it is quite possible that your New-AzKeyVault Cmdlet fails.
I'm not able to read the value of one of my secrets in Key Vault. I'm logged in with my Azure account and I have full permission to the selected Key Vault.
I'm able to retrieve a list of available secrets using the following command:
$keyVaultValue = (Get-AzKeyVaultSecret -VaultName 'name-of-key-vault')
And then see the content when I write:
Write-Output $keyVaultValue
But when I ask for a specific key it just returns null:
$keyVaultValue = (Get-AzKeyVaultSecret -VaultName 'name-of-key-vault' -Name 'my-secret-name').SecretValueText
I've checked the name and subscription ID and everything is correct. I can easily read the value from the portal, but no from powershell on my Windows PC.
Any suggestions?
SecretValueText is deprecated, You can use the following syntax the retrieve the value as plain text:
$keyVaultValue = Get-AzKeyVaultSecret -VaultName 'name-of-key-vault' -Name 'my-secret-name'
$keyVaultValue.SecretValue | ConvertFrom-SecureString -AsPlainText
More information and examples can be found here.
If you want to show all key-vault secrets name and their key values then you can use this in powershell
$secrets=Get-AzKeyVaultSecret -VaultName 'key-vault-name'
$secrets | % {Write-Output "$($_.name) $($(Get-AzKeyVaultSecret -VaultName $_.VaultName -Name $_.Name).SecretValue | ConvertFrom-SecureString -AsPlainText)" }
Try using this function:
function GetSecretValue
{
param(
[String]$keyvaultName,
[String]$secretName
)
Write-Host "Retrieving secret $secretName from $keyvaultName... " -NoNewline
if ((Get-Command Get-AzKeyVaultSecret).ParameterSets.Parameters.Name -contains "AsPlainText")
{
# Newer Get-AzKeyVaultSecret version requires -AsPlainText parameter
$secretValue = Get-AzKeyVaultSecret -VaultName $keyvaultName -Name $secretName -AsPlainText
}
else
{
$secretValue = (Get-AzKeyVaultSecret -VaultName $keyvaultName -Name $secretName).SecretValueText
}
Write-Host "ok"
return $secretValue
}
Usage example:
$keyVaultValue = GetSecretValue "name-of-key-vault" "my-secret-name"
I am trying to copy a certificate from one key vault into another without persisting it on local machine. I looks like Azure KeyVault CLI only supports file-based certificate import like this:
# download cert
az keyvault certificate download --file $certFileName --vault-name $sourceAkv -n $certName
# import cert
az keyvault certificate import --file $certFileName --vault-name $destAkv -n $certName
Is there any way to pass an object or string instead? In Azure Powershell module this is possible:
Import-AzureKeyVaultCertificate -VaultName $DestinationVaultName -Name $CertificateName -CertificateString $secretText.SecretValueText
Thoughts?
I found something like this can work in PowerShell to export the certificate and key as an object, then import it into another keyvault without having to save it as a temporary PFX file.
$CertName = 'mycert'
$SrcVault = 'mysourcekeyvault'
$DstVault = 'mydestinationkeyvault'
$cert = Get-AzKeyVaultCertificate -VaultName $SrcVault -Name $CertName
$secret = Get-AzKeyVaultSecret -VaultName $SrcVault -Name $cert.Name
$secretValueText = '';
$ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secret.SecretValue)
try {
$secretValueText = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr)
}
finally {
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr)
}
$secretByte = [Convert]::FromBase64String($secretValueText)
$x509Cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2
$x509Cert.Import($secretByte, "", "Exportable,PersistKeySet")
Import-AzKeyVaultCertificate -VaultName $DstVault -Name $CertName -CertificateCollection $x509Cert
It seems that there is no way to just import the certificate context to the Azure Keyvault as you say. It only has the parameter dependant on the certificate file.
I was trying to Create a Application in Azure AD with Azure PowerShell Certificate authentication, below is the Powershell snippet:
Login-AzureRmAccount
$certPassword = ConvertTo-SecureString $CertPassword -AsPlainText -Force
$x509 = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $certPath,$certPassword
$credValue = [System.Convert]::ToBase64String($x509.GetRawCertData())
$adapp = New-AzureRmADApplication -DisplayName $ApplicationName -HomePage $URL -IdentifierUris $URL -CertValue $credValue -StartDate $startDate -EndDate $endDate
$sp = New-AzureRmADServicePrincipal -ApplicationId $adapp.ApplicationId
Set-AzureRmKeyVaultAccessPolicy -VaultName $VaultName -ServicePrincipalName $sp.ServicePrincipalNames[1] -PermissionsToKeys all –PermissionsToSecrets all -ResourceGroupName $ResourceGroupName
The Azure AD application was created successfully, however for Azure AD application with Certificate Authentication, the customKeyIdentifier and value of in the keyCredentials is null after creation, this is the portion of manifest of my application I downloaded from Azure portal:
"keyCredentials": [{
"customKeyIdentifier": null,
"endDate": "2018-01-25T11:55:35.7680698Z",
"keyId": "ca1e536c-2220-478b-af73-1198d125bb5f",
"startDate": "2017-01-25T11:55:35.7680698Z",
"type": "AsymmetricX509Cert",
"usage": "Verify",
"value": null
} ]
The certificate is a self signed certificate created using makecert command generated locally.
I am using Powershell Version of 2.0.1
C# Code to retrieve the token with Application Id & Thumbprint
public static async Task GetAccessToken(string authority,
string resource, string scope) {
var context = new AuthenticationContext(authority, TokenCache.DefaultShared);
var result = await context.AcquireTokenAsync(resource, AssertionCert);
return result.AccessToken; }
This Code errors out at var result with "Keyset does not exists"
Is there any way to resolve this issue?
Thank you :)
Did you look at the answer here?
Create a Application in Azure AD with Azure PowerShell Certificate authentication
In the comments he mentions that CustomKeyIdentifier being null does not matter for authentication.
Did you try authenticating regardless of the null value?\
EDIT:
If you want to generate a thumbprint for a public certificate you own, you can do so using the following powershell cmdlets:
$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import(“mycer.cer”)
$bin = $cer.GetCertHash()
$base64Thumbprint = [System.Convert]::ToBase64String($bin)
I hope this helps.