Creating a Microsoft.KeyVault/vaults/certificates #10044
-
|
Bicep version Describe the bug To Reproduce
param keyVaultName string = 'kv-${uniqueString(resourceGroup().id)}'
param certificateName string = 'cert-${uniqueString(resourceGroup().id)}'
param issuerName string = 'Self'
param subjectName string = 'CN=contoso.com'
resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' = {
name: keyVaultName
location: resourceGroup().location
properties: {
sku: {
name: 'standard'
family: 'A'
}
tenantId: subscription().tenantId
accessPolicies: [
{
tenantId: subscription().tenantId
objectId: 'f520d84c-3fd3-4cc8-88d4-2ed25b00d27a'
permissions: {
keys: [
'get'
'create'
'delete'
'list'
'update'
'import'
'backup'
'restore'
'recover'
'purge'
]
secrets: [
'get'
'list'
'set'
'delete'
'backup'
'restore'
'recover'
'purge'
]
certificates: [
'get'
'list'
'delete'
'create'
'import'
'update'
'managecontacts'
'manageissuers'
'getissuers'
'listissuers'
'setissuers'
'deleteissuers'
'purge'
'recover'
]
}
}
]
enableSoftDelete: true
softDeleteRetentionInDays: 90
enableRbacAuthorization: false
networkAcls: {
defaultAction: 'Allow'
bypass: 'AzureServices'
}
}
}
resource certificate 'Microsoft.KeyVault/vaults/certificates@2021-06-01-preview' = {
name: '${keyVault.name}/${certificateName}'
properties: {
certificatePolicy: {
issuerParameters: {
name: 'Self'
certificateTransparency: null
}
x509CertificateProperties: {
subject: 'CN=wabbit-networks.io,O=Notary,L=Seattle,ST=WA,C=US'
validityInMonths: 60
enhancedKeyUsage: [ 'ServerAuthentication' ]
keyUsage: [ 'digitalSignature' ]
ekus: [ '1.3.6.1.5.5.7.3.3' ]
}
}
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 7 comments 2 replies
-
|
These api's are not published at this time... via ARM... You can only access via REST enpoints etc. TF, az cli, powershell. https://learn.microsoft.com/en-us/azure/templates/microsoft.keyvault/allversions
We have this open issue for this: in the meantime I use deployment scripts executed from Bicep... only downside is that i cannot enable my keyvault firewall because of this. Example of requesting the cert .. https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/SFM-Cluster.bicep#L78 module createCertswithRotation 'x.newCertificatewithRotation.ps1.bicep' = { // if( Global.DomainNameExt != 'psthing.com') {
name: toLower('dp-createCert-${sfmname}')
params: {
userAssignedIdentityName: UAICert.name
CertName: sfmname
Force: false
SubjectName: 'CN=${commonName}'
VaultName: KV.name
DnsNames: union(array(commonName), array(friendlyName), array(fullName), array(shortName))
}
}The module to call the deployment script. The PowerShell used in the deployment script.
|
Beta Was this translation helpful? Give feedback.
-
|
Bump |
Beta Was this translation helpful? Give feedback.
-
|
It's mid-2025 and we still can't use Bicep to deploy a certificate resource? 🤔 The concept is so widespread that even some versions of Copilot/GPT will say "Yeah, Deployment scripts are a good workaround, but we need |
Beta Was this translation helpful? Give feedback.
-
|
Is this in the works? Any ETA? |
Beta Was this translation helpful? Give feedback.
-
|
Microsoft, please. |
Beta Was this translation helpful? Give feedback.
-
|
Here is how I deploy certs to be used in an app service with a custom hostname, I think it only works when you have modules, not direct resource definitions. This allows you to store the cert in the KV and then associate it to the app service. Note: We use Azure Pipelines, so its combination of PowerShell scripts and bicep deployment to accomplish this: Azure Pipelines task to download the pfx file from secure files, then base64encode it and store it as a pipeline variable, you could in theory do this in PowerShell manually, get the base64 value and set it as a plain secret in the Library/Variable group and consume that by itself just a bigger pain to manage then just uploading a new cert to the secure files and let he pipeline do its magic. steps:
- task: DownloadSecureFile@1
name: downloadSecureFile
displayName: 'Download Certificate'
inputs:
secureFile: ${{ parameters.components.secureFileName }}
- task: AzurePowerShell@5
displayName: '🔐 Create Certificate Secret'
inputs:
azureSubscription: ${{ parameters.components.azureSubscription }}
azurePowerShellVersion: LatestVersion
pwsh: true
scriptType: 'InlineScript'
Inline: | # powershell
$pfxFilePath = '$(downloadSecureFile.secureFilePath)'
Write-Color "`nCreating Certificate Secret from the file: ","$pfxFilePath" -Color Green, Gray, Green, Gray, Green -ANSI
$pwd = "$(CertPassword)"
$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)
$secret = [System.Convert]::ToBase64String($clearBytes)
Write-Host "##vso[task.setvariable variable=CertificateSecret;issecret=true]$secret"The variable that is set is what we use in the bicep code, we use a replace tokens task that does variable substitution in the main.bicep/bicepparam file. main.bicepparam file definition: param configKeyVaultSecrets = {
keyVaultName: '#{keyVaultName}' // token replace #{} with variable value for keyVaultName
secrets: [
{
name: 'WildcardCert'
value: '#{CertificateSecret}'
contentType: 'application/x-pkcs12'
}
]
}
param configHostnames = {
dnsZone: {
subscription: 'xxx-xx-xxx-xxx'
resourceGroup: '<rgName>'
name: '#{dnsZone}'
type: 'public'
}
certificateName: 'WildcardCert'
bindings: [
{
webAppName: configWebApp.webSiteName
customHostName: '#{customfehostname}'
}
{
webAppName: configWebApi.webSiteName
customHostName: '#{custombehostname}'
}
]
}We have a module to deploy custom hostnames, as well as hostname binding module and dnszonerecord creation module that run before this cerate the necessary items then assign cert and custom hostname: customhostname module: @minLength(2)
@maxLength(60)
@description('The WebApp/App Service name.')
param webAppName string
@description('The location of the resource. Defaults to the resource group location when not specified.')
param location string = resourceGroup().location
@minLength(2)
@maxLength(60)
@description('The existing App Service Plan name.')
param appServicePlanName string
@minLength(2)
@maxLength(63)
@description('The Custom Domain name for the App Service.')
param customHostName string
@minLength(2)
@maxLength(63)
@description('The DNS Zone to assign to.')
param dnsZoneName string
@description('All resources supposed to have tags by the policy')
param tags object
@minLength(2)
@maxLength(24)
@description('The name of the Key Vault to use for storing certificates.')
param keyVaultName string?
@minLength(2)
@maxLength(127)
@description('The name of the certificate stored in the key vault as a key vault secret.')
param certificateName string?
/*
/// main.bicep - Param Declaration ///
@description('Configuration settings for the Web App custom hostname binding.')
param configHostnameBinding object
/// main.bicep - Module Definition ///
module customHostName 'br/GC:customhostname:latest' = {
name: '${configHostnameBinding.customHostName}-${configHostnameBinding.webAppName}-ssl'
params: {
tags: tags
dnsZoneName: configHostnameBinding.dnsZoneName
appServicePlanName: configHostnameBinding.appServicePlanName
webAppName: configHostnameBinding.webAppName
customHostName: configHostnameBinding.customHostName
keyVaultName: configHostnameBinding.keyVaultName
certificateName: configHostnameBinding.certificateName
}
}
/// main.bicepparam - Param Definition ///
param configHostnameBinding = {
webAppName: '{#webAppName}'
appServicePlanName: '{#appServicePlanName}'
customHostName: 'bicep-wildcard-cert'
dnsZoneName: 'example.com'
certificateName: 'wildcard-cert'
keyVaultName: '{#keyVaultName}'
}
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
resource webApp 'Microsoft.Web/sites@2024-11-01' existing = {
name: webAppName
}
resource appServicePlan 'Microsoft.Web/serverfarms@2024-11-01' existing = {
name: appServicePlanName
}
resource keyVault 'Microsoft.KeyVault/vaults@2024-11-01' existing = if (!empty(keyVaultName)) {
name: keyVaultName ?? 'placeholder'
}
resource certificate 'Microsoft.Web/certificates@2024-11-01' = {
name: (empty(keyVaultName)) ? '${customHostName}-${webAppName}' : '${customHostName}-${webAppName}-pkicert'
location: location
tags: tags
properties: (empty(keyVaultName))
? {
canonicalName: '${customHostName}.${dnsZoneName}'
serverFarmId: appServicePlan.id
}
: {
serverFarmId: appServicePlan.id
keyVaultId: keyVault.id
keyVaultSecretName: certificateName
}
}
resource customHostname 'Microsoft.Web/sites/hostNameBindings@2024-11-01' = {
parent: webApp
name: '${customHostName}.${dnsZoneName}'
properties: (empty(keyVaultName))
? {
hostNameType: 'Verified'
sslState: 'SniEnabled'
thumbprint: certificate.properties.thumbprint
}
: {
sslState: 'SniEnabled'
thumbprint: certificate.properties.thumbprint
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@description('The ID of the custom hostname binding.')
output id string = customHostname.id
@description('The name of the custom hostname binding.')
output name string = customHostname.namemain.bicep module defintion: @batchSize(1)
module customHostNames 'br/GC:customhostname:latest' = [
for binding in configHostnames.bindings: {
name: '${binding.customHostName}-custom-hostname-ssl'
dependsOn: [hostnameBindings]
params: {
tags: tags
dnsZoneName: configHostnames.dnsZone.name
appServicePlanName: appServicePlan.outputs.name
webAppName: binding.webAppName
customHostName: binding.customHostName
keyVaultName: keyVault.outputs.name
certificateName: configHostnames.certificateName
}
}
] |
Beta Was this translation helpful? Give feedback.

These api's are not published at this time... via ARM... You can only access via REST enpoints etc.
TF, az cli, powershell.
https://learn.microsoft.com/en-us/azure/templates/microsoft.keyvault/allversions
We have this open issue for this:
in the meantime I use deployment scripts executed from Bicep... only downside is that i cannot enable my keyvault firewall because of this.
Example of requesting the cert ..
https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/SFM-Cluster.bicep#L78