App service certificate is not being updated in KeyVault - azure-web-app-service

We have an app service certificate which is set to renew automatically every year.
According to the overview and the timeline, a new certificate has already been created about a month ago.
However, when we try to export the certificate from KeyVault, the new version is not there. Only the old versions are listed.
We already tried to rekey the certificate, without any change.
What could be the cause of this?
Update:
After looking at https://resources.azure.com, I realized that the connection to KeyVault has broken down. No idea how to fix that...
"properties": {
"keyVaultId": "...",
"keyVaultSecretName": "...",
"provisioningState": "AzureServiceUnauthorizedToAccessKeyVault"
}

The reason was that the access policies to the KeyVault got lost, probably during a subscription migration.
We fixed it like this:
Login-AzureRmAccount
Set-AzureRmContext -SubscriptionId AZURE_SUBSCRIPTION_ID
# Microsoft.CertificateRegistration
Set-AzureRmKeyVaultAccessPolicy -VaultName KEY_VAULT_NAME -ServicePrincipalName f3c21649-0979-4721-ac85-b0216b2cf413 -PermissionsToSecrets get,set,delete
# Microsoft.Web
Set-AzureRmKeyVaultAccessPolicy -VaultName KEY_VAULT_NAME -ServicePrincipalName abfa0a7c-a6b6-4736-8310-5855508787cd -PermissionsToSecrets get
Credit to this for providing us with the correct service principal names.

Related

Automatically update Service Principal client secret on expiry?

I have a Service Principal for a Power Platform environment which will be used by a DevOps platform to make deployments to this environment.
The service principal requires me to set a client secret which will be referenced by my Service Connection in DevOps
You must set a client secret expiry date of up to 2 years and after that time, it won't work. So I would need to go into the Azure portal, update the client secret and then into DevOps and update the service connection.
Is there a way I can do this automatically?
You must set a client secret expiry date of up to 2 years and after that time, it won't work.
Actually, no need to do that, in azure portal, the maximum is 2 years, but you could use azure powershell to create a near-permanent secret, e.g. 100 years.
If you want to custom the secret value, use Az module, login with Connect-AzAccount, then use New-AzADAppCredential as below.
$SecureStringPassword = ConvertTo-SecureString -String "password" -AsPlainText -Force
New-AzADAppCredential -ApplicationId <ApplicationId of the App Registration> -CustomKeyIdentifier "test" -Password $SecureStringPassword -EndDate (Get-Date).AddYears(100)
If you want to generate a secret value automatically, use AzureAD module, login with Connect-AzureAD, then use as New-AzureADApplicationPasswordCredential below.
New-AzureADApplicationPasswordCredential -ObjectId <ObjectId of the App Registration> -EndDate (Get-Date).AddYears(100)

Adding key vault access permissions while preserving existing ones

I have an Azure PowerShell task in my pipeline, in which I need to import a certificate to a key vault. Before doing that, I need to assign Import certificate permission to the current service principal. However, this service principal might already have existing certificate permissions (e.g. Get, List) from other tasks in this or other pipelines. If I use Set-AzKeyVaultAccessPolicy, it will remove these other permissions. Is there a way of preserving these permissions, and just adding some new ones?
$spId = (Get-AzContext).Account.Id;
Set-AzKeyVaultAccessPolicy -VaultName $kv -ServicePrincipalName $spId -PermissionsToCertificates Import
Import-AzKeyVaultCertificate -VaultName $kv …
There is no direct way to add the new permission, your option is to get the old permissions as a list, add the new permission to it, then set all the permissions again.
The sample works for me:
$spId = (Get-AzContext).Account.Id
$objectid = (Get-AzADServicePrincipal -ApplicationId $spId).Id
$kv = Get-AzKeyVault -ResourceGroupName <group-name> -VaultName joykeyvault
$cerpermission = ($kv.AccessPolicies | Where-Object {$_.ObjectId -eq $objectid}).PermissionsToCertificates
$cerpermission += "Import"
Set-AzKeyVaultAccessPolicy -VaultName joykeyvault -ObjectId $objectid -BypassObjectIdValidation -PermissionsToCertificates $cerpermission
Note: The parameters in the last line is important, if your service principal used in the devops service connection does not have the permission to list service principals in your AAD tenant, please use -ObjectId $objectid -BypassObjectIdValidation instead of -ServicePrincipalName $spId, otherwise you will get an error.

Authenticate to Azure Keyvault using Certificate and get secret

I am looking for an example of using a certificate to authenticate to the keyvault, and then get a secret -- all in PowerShell (already have operational C#).
Have an app in AD for accessing Keyvault.
First, make sure your AD App(service principal) has the correct permission in your keyvault -> Access policies, in your case, it should be Get and List secret permissions.
Then get values for signing in and try the command as below.
Connect-AzAccount -CertificateThumbprint "<certificate Thumbprint>" -ApplicationId "<AD App applicationid(clientid)>" -Tenant "<tenant id>" -ServicePrincipal
Get-AzKeyVaultSecret -VaultName "<keyvault name>" -Name "<secret name>" -Version "<secret version>" | ConvertTo-Json

How do I define a certificate resource in my ARM template when it is hosted in the Key Vault?

I am trying to define an ARM template for my resource group. Ultimately I'm trying to replicate what I have to do manually by navigating to the SSL certificates tab for an App Service within the portal.
I've uploaded a PFX file to the Secrets tab of my KeyVault. I've granted Get access to the global RM service principal.
At the moment this is what my Microsoft.Web/certificates resource looks like in my template. It is just defined as a resource at the top level of the resource group, and not as a sub-resource of a website or anything like that:
{
"type":"Microsoft.Web/certificates",
"name": "signingCredentials",
"location": "[parameters('region')]",
"apiVersion": "2015-08-01",
"properties": {
"keyVaultId": "<My KeyVault ID>",
"keyVaultSecretName": "<My Secret Name>"
}
}
When I attempt to deploy this template I receive the message:
The parameter KeyVault Certificate has an invalid value
I haven't been able to find any documentation on this parameter and what value it would be expecting. I'm assuming it's missing from the properties section in the resource. So far anything I've found on the subject only references keyVaultId and keyVaultSecretName.
What am I doing wrong? Is what I'm trying to accomplish even possible?
The problem does not appear to be caused by my template, but something with how the certificate was uploaded to the KeyVault through the UI. This article provided me a script to upload the file directly to the KeyVault using powershell.
$pfxFilePath = "F:\KeyVault\PrivateCertificate.pfx"
$pwd = "[2+)t^BgfYZ2C0WAu__gw["
$flag = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$collection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$collection.Import($pfxFilePath, $pwd, $flag)
$pkcs12ContentType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12
$clearBytes = $collection.Export($pkcs12ContentType)
$fileContentEncoded = [System.Convert]::ToBase64String($clearBytes)
$secret = ConvertTo-SecureString -String $fileContentEncoded -AsPlainText –Force
$secretContentType = 'application/x-pkcs12'
Set-AzureKeyVaultSecret -VaultName akurmitestvault -Name keyVaultCert -SecretValue $Secret -ContentType $secretContentType # Change the Key Vault name and secret name
Using the Get-AzureKeyVault script from Jambor's answer, I am unable to see any difference between the certificate uploaded in the UI. I even changed the content type of my uploaded certificate from Certificate to application/x-pcks2 and it still did not work. Seems like it might possibly a bug in the UI, or just a difference in how the powershell script handles it.
The parameter KeyVault Certificate has an invalid value
It seems that this issue is not caused by your template. We can refer to this article to check it. From the error message, it shows me that the certification name is incorrect. We can use Get-AzureKeyVaultSecret to get its name. The following is details:
As above screenshot, the value "kvcertificate" is the value we expected.

Azure Key Vault Access Policy Doesn't Work For Groups

Access policies via groups on Azure Key Vault don't seem to work.
If I create a new key vault
New-AzureRmKeyVault -VaultName $vaultName
And check the keys (which there aren't any of currently)
Get-AzureKeyVaultKey -VaultName $vaultName
That works.
If I add access to a group that the current user is a member of
$group = (Get-AzureRmADGroup -SearchString 'All Developers')[0].Id
Set-AzureRmKeyVaultAccessPolicy -VaultName $vaultName -ResourceGroupName $resourceGroupName -ObjectId $group -PermissionsToKeys all -PermissionsToSecrets all
And remove direct access
Remove-AzureRmKeyVaultAccessPolicy -VaultName $vaultName -ResourceGroupName $resourceGroupName -UserPrincipalName $upn
The list operation now fails
Get-AzureRmKeyVault -VaultName $vaultName -ResourceGroupName $resourceGroupName
Get-AzureKeyVaultKey : Operation "list" is not allowed
How can I permission by group?
I discovered today that it works for users in permissioned group objects. Doesn't work for service principals in those groups.
In other words, if I authenticate using a client id and client secret, the associated service principal must have an access policy directly set on the key vault. If I permission a security group, a user in that group can in fact access the key vault. I guess this has something to do with how the JWT includes security groups in it with users, but not service principals...
The reason that adding an access policy to a group is that it isn't supported. If you look at the help for Set-AzureRmKeyVaultAccessPolicy there is this for ObjectId
-ObjectId <Guid>
Specifies the object ID of the user or service principal in Azure Active Directory for which to grant permissions.
Required? true
Position? named
Default value none
Accept pipeline input? true(ByPropertyName)
Accept wildcard characters? false
As you can see ObjectId only supports either Service principals or users.
This is reflected in the parameters of the source code for Set-AzureRmKeyVaultAccessPolicy and further up the chain the REST API when posting to
https://management.azure.com/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/Microsoft.KeyVault/vaults/{vault-name}?api-version={api-version}
The payload contains the objectId parameter which is defined as
Specifies the object ID of a user or service principal in the Azure Active Directory tenant for the vault. The ID must be specified as a GUID.
I would imagine that this functionality will be added at some point in future, but at the moment it isn't possible.
This Access Denied / 403 Forbidden error can also happen when an app has made requests to a Key Vault before it was added to the Azure Active Directory Group.
Perhaps this has something to do with caching of service principal information on the App Service instance? I was unable to find documentation of this.
Solution: restart the App Service.

Resources