I am trying to deploy a number of three azure storage resources under two storage accounts, and I want to implement three private endpoints as to only allow connection to these resources from VMs in the same VNET. Type of resources that need to be connected to in two separate storage accounts are (per storage account):
storageAccountTemp
Azure blob queue
Azure blob storage
storageAccountDatalake
ADLS 2 containers (datalake)
I have the following Azure Bicep code for deploying the permanent and temporary stores:
param location string
param environmentType string
param storageAccountSku string
param privateEndpointsSubnetId string
var privateEndpointNameTmpstBlob = 'pe-tmpst-blob-${environmentType}-001'
var privateEndpointNameTmpstQueue = 'pe-tmpst-queue-{environmentType}-001'
var privateEndpointNamePst = 'pe-pst-${environmentType}-001'
/// Temp storage ///
resource storageAccountTemp 'Microsoft.Storage/storageAccounts#2021-08-01' = {
name: 'tmpst${environmentType}'
location: location
sku: {
name: storageAccountSku
}
kind: 'StorageV2'
properties: {
allowBlobPublicAccess: false
accessTier: 'Hot'
minimumTlsVersion: 'TLS1_0'
publicNetworkAccess: 'Disabled'
}
}
resource blobContainerForQueue 'Microsoft.Storage/storageAccounts/blobServices/containers#2021-08-01' = {
name: '${storageAccountTemp.name}/default/claimcheck-storage-${environmentType}'
properties: {
publicAccess: 'None'
}
}
resource storageQueueMain 'Microsoft.Storage/storageAccounts/queueServices/queues#2019-06-01' = {
name: '${storageAccountTemp.name}/default/queue-main-${environmentType}'
}
/// Persistant storage datalake ///
resource storageAccountDatalake 'Microsoft.Storage/storageAccounts#2021-08-01' = {
name: 'pstdatalake${environmentType}'
location: location
sku: {
name: storageAccountSku
}
kind: 'StorageV2'
properties: {
allowBlobPublicAccess: false
accessTier: 'Hot'
minimumTlsVersion: 'TLS1_0'
isHnsEnabled: true
publicNetworkAccess: 'Disabled'
}
}
/// Data///
resource ContainerForData 'Microsoft.Storage/storageAccounts/blobServices/containers#2021-08-01' = {
name: '${storageAccountDatalake.name}/default/data-${environmentType}'
properties: {
publicAccess: 'None'
}
}
/// Private endpoints configuration for tempblob, queue and datalake ///
resource privateEndpointTmpstBlob 'Microsoft.Network/privateEndpoints#2021-05-01' = if (environmentType == 'dev' || environmentType == 'prd') {
name: privateEndpointNameTmpstBlob
location: location
properties: {
subnet: {
id: privateEndpointsSubnetId
}
privateLinkServiceConnections: [
{
name: privateEndpointNameTmpstBlob
properties: {
privateLinkServiceId: storageAccountTemp.id
groupIds: ['blob']
}
}
]
}
}
resource privateEndpointTmpstQueue 'Microsoft.Network/privateEndpoints#2021-05-01' = if (environmentType == 'dev' || environmentType == 'prd') {
name: privateEndpointNameTmpstQueue
location: location
properties: {
subnet: {
id: privateEndpointsSubnetId
}
privateLinkServiceConnections: [
{
name: privateEndpointNameTmpstQueue
properties: {
privateLinkServiceId: storageAccountTemp.id
groupIds: ['queue']
}
}
]
}
}
resource privateEndpointPst 'Microsoft.Network/privateEndpoints#2021-05-01' = if (environmentType == 'dev' || environmentType == 'prd') {
name: privateEndpointNamePst
location: location
properties: {
subnet: {
id: privateEndpointsSubnetId
}
privateLinkServiceConnections: [
{
name: privateEndpointNamePst
properties: {
privateLinkServiceId: storageAccountDatalake.id
groupIds: ['blob']
}
}
]
}
}
As you can see, for the storage account, IsHnsEnabled is set to true, as to enable HierarchicalNamespace and thus ADLS2 functionality. The problem is, if I include the privateEndpointPst resource deployment in the Bicep deployment, and then try to view a datalake container in the portal from a VM that is in the same VNET as the private endpoint (which are in the subnet that makes the privateEndpointsSubnetId variable), I get the following message when trying to look at files in one of the datalake containers:
I believe it is not the problems in the picture. The reason for this is that when I deploy all three endpoints together, they all show this same problem when trying to look at blob/queue/datalake in storageAccountTemp and storageAccountDatalake when I deploy all three endpoints.
However, only deploying the two endpoints for the storageAccountTemp resources and not the one for Datalake, I can see the data in the portal when running from the VM in the VNET and code running from this VM can also reach the queue + blob. So not only does the deployment of the privateEndpointPst seem to mess up datalake reachability, it also in some way does the same to the reachability of my other queue and blob in the storageAccountTemp if I deploy them altogether. My mind is boggled as to why this is happening, and why I cannot seem to deploy the datalake endpoint in the right way. Also, sometimes, deploying the endpoints altogether WILL make the datalake endpoint work, and break the other two, which is even more mind-boggling. Clicking do you want to do some checks to detect common connectivity issues gives me the following information, which does not make me much wiser as to what is causing the issue (since I'm pretty sure it's not firewalls; sometimes I can access, sometimes not):
Does anyone see what could be wrong with my Bicep code for deploying the endpoint that might be causing this issue? I'm at quite a loss here. Also tried to replace groupIds: ['blob'] with groupIds: ['dfs'], but that does not seem to solve my problem.
I seem to have found the issue. For connecting to a datalake resource, one needs to have both a private endpoint with groupIds: ['blob'] and groupIds: ['dfs], since the blob API is still used for getting some meta-info about the containers (as far as I can understand).
So adding:
resource privateEndpointPstDfs 'Microsoft.Network/privateEndpoints#2021-05-01' = if (environmentType == 'dev' || environmentType == 'prd') {
name: privateEndpointNamePstDfs
location: location
properties: {
subnet: {
id: privateEndpointsSubnetId
}
privateLinkServiceConnections: [
{
name: privateEndpointNamePstDfs
properties: {
privateLinkServiceId: storageAccountDatalake.id
groupIds: ['dfs']
}
}
]
}
}
Made the deployment work successfully.
Related
I am trialling the use of Bicep and container apps in my organisation and we have separated out concerns within the SAME tenant but in different subscriptions like so:
Development
Production
Management
I want to be able to deploy each of these subscriptions using Bicep scripts (individual ones per subscription) and ideally only use managed identity for security.
Within the management subscription we have an ACR which has the admin account intentionally disabled as I don't want to pull via username/password. Question one, should this be possible? As it seems that we should be able to configure an AcrPull role against the container app(s) without too much trouble.
The idea being that the moment the container app is deployed it pulls from the Acr and is actively useable. I don't want an intermediary such as Azure DevOps handling the orchestration for example.
In bicep I've successfully configured the workspace, container environment but upon deploying my actual app I'm a bit stuck - it fails for some incomprehensible error message which I'm still digging into. I've found plenty of examples using the admin/password approach but documentation for alternatives appears lacking which makes me worry if I'm after something that isn't feasible. Perhaps user identity is my solution?
My bicep script (whilst testing against admin/password) looks like this:
name: containerAppName
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
managedEnvironmentId: containerAppEnvId
configuration: {
secrets: [
{
name: 'container-registry-password'
value: containerRegistry.listCredentials().passwords[0].value
}
]
ingress: {
external: true
targetPort: targetPort
allowInsecure: false
traffic: [
{
latestRevision: true
weight: 100
}
]
}
registries: [
{
server: '${registryName}.azurecr.io'
username: containerRegistry.listCredentials().username
passwordSecretRef: 'container-registry-password'
}
]
}
template: {
revisionSuffix: 'firstrevision'
containers: [
{
name: containerAppName
image: containerImage
resources: {
cpu: json(cpuCore)
memory: '${memorySize}Gi'
}
}
]
scale: {
minReplicas: minReplicas
maxReplicas: maxReplicas
}
}
}
}
However this is following an admin/password approach. For using managed identity, firstly do I need to put a registry entry in there?
``` registries: [
{
server: '${registryName}.azurecr.io'
username: containerRegistry.listCredentials().username
passwordSecretRef: 'container-registry-password'
}
]
If so, the listCredentials().username obviously won't work with admin/password disabled. Secondly, what would I then need in the containers section
containers: [
{
name: containerAppName
image: containerImage ??
resources: {
cpu: json(cpuCore)
memory: '${memorySize}Gi'
}
}
]
As there appears to be no mention of the need for pointing at a repository, or indeed specifying anything other than a password/admin account. Is it that my requirement is impossible as the container app needs to be provisioned before managed identity can be applied to it? Is this a chicken vs egg problem?
You could use a user-assigned identity:
Create a user assigned identity
Grant permission to the user-assigned identity
Assign the identity to the container app
# container-registry-role-assignment.bicep
param registryName string
param roleId string
param principalId string
// Get a reference to the existing registry
resource registry 'Microsoft.ContainerRegistry/registries#2021-06-01-preview' existing = {
name: registryName
}
// Create role assignment
resource roleAssignment 'Microsoft.Authorization/roleAssignments#2020-04-01-preview' = {
name: guid(registry.id, roleId, principalId)
scope: registry
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleId)
principalId: principalId
principalType: 'ServicePrincipal'
}
}
Then from your main:
param name string
param identityName string
param environmentName string
param containerImage string
param location string = resourceGroup().location
param containerRegistrySubscriptionId string = subscription().subscriptionId
param containerRegistryResourceGroupName string = resourceGroup().name
param containerRegistryName string
// Create identtiy
resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities#2022-01-31-preview' = {
name: identityName
location: location
}
// Assign AcrPull permission
module roleAssignment 'container-registry-role-assignment.bicep' = {
name: 'container-registry-role-assignment'
scope: resourceGroup(containerRegistrySubscriptionId, containerRegistryResourceGroupName)
params: {
roleId: '7f951dda-4ed3-4680-a7ca-43fe172d538d' // AcrPull
principalId: identity.properties.principalId
registryName: containerRegistryName
}
}
// Get a reference to the container app environment
resource managedEnvironment 'Microsoft.App/managedEnvironments#2022-03-01' existing = {
name: environmentName
}
// create the container app
resource containerapp 'Microsoft.App/containerApps#2022-03-01' = {
dependsOn:[
roleAssignment
]
name: name
...
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${identity.id}': {}
}
}
properties: {
managedEnvironmentId: managedEnvironment.id
configuration: {
...
registries: [
{
server: '${containerRegistryName}.azurecr.io'
identity: identity.id
}
]
}
template: {
...
containers: [
{
name: name
image: '${containerRegistryName}.azurecr.io/${containerImage}'
...
}
]
}
}
}
is it possible to retrieve a Storage Account's Access Key when deploying the Storage Account via a Bicep module?
My parent bicep creates a storage account using a module file, and it then needs an Access Key but I cannot get it working in a way that's secure:
Parent Bicep
module functionAppStorageModule 'storage-account.bicep' = {
name: 'functionAppStorage'
params: {
...
}
}
resource functionApp 'Microsoft.Web/sites#2021-03-01' = {
name: functionAppName
location: location
kind: 'functionapp'
properties: {
siteConfig: {
appSettings: [
{
name: 'store_key'
value: ???
}
]
}
}
}
I can get it working if I set an output on the module file, and use that output in the parent bicep:
Module Bicep
output storageAccountStr string = 'AccountKey=${listKeys(storageAccount.id, storageAccount.apiVersion).keys[0].value}'
Parent Bicep
properties: {
siteConfig: {
appSettings: [
{
name: 'store_key'
value: functionAppStorageModule.outputs.storageAccountStr
}
]
}
}
But this does not seem secure to me as the key appears in plain text in Deployments' Output section on the Azure portal.
Alternatively, I may work around by deploying the storage account beforehand without the use of a module file, as the use of modules seems to be the issue, but just would like to know what I'm trying above is impossible?
Thanks
If you create the function app in a different module, this should work.
storage-account.bicep file:
param storageAccountName string
...
// Create the storage account
resource storageAccount 'Microsoft.Storage/storageAccounts#2021-09-01' = {
name: storageAccountName
...
}
// return the name
output name string = storageAccount.name
function-app.bicep file:
...
param storageAccountName string
// Get a reference to the existing storage
resource storageAccount 'Microsoft.Storage/storageAccounts#2021-09-01' existing = {
name: storageAccountName
}
// Create the function app
resource functionApp 'Microsoft.Web/sites#2021-03-01' = {
...
properties: {
siteConfig: {
appSettings: [
{
name: 'store_key'
// Here we can securely get the access key
value: 'AccountKey=${listKeys(storageAccount.id, storageAccount.apiVersion).keys[0].value}'
}
]
}
}
}
Then in your main.bicep:
// Create the storage account
module storage 'storage-account.bicep' = {
name: 'functionAppStorage'
params: {
storageAccountName: storageAccountName
...
}
}
// create the function app once the storage has been created
module functionApp 'function-app.bicep' = {
name: 'functionApp'
params: {
...
// depends on storage module
storageAccountName: storage.outputs.name
}
}
I am working on building an azure bicep file to deploy Azure resources.
Within the same bicep file, I am creating a storage account, a couple of containers, and some management policies. Within the Microsoft documentation:
Before you configure a lifecycle management policy, you can choose to enable blob access time tracking. When access time tracking is enabled, a lifecycle management policy can include an action based on the time that the blob was last accessed with reading or write operation.
Though I am following the documentation in terms of enabling the last access time tracking policy according to the documentation, and running the management policies within the same bicep file, I am still running into this error:
"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details. Last access time based tracking policy must be enabled before using its specific actions in object lifecycle management policy"
Here is my bicep file:
resource storage_account_blob 'Microsoft.Storage/storageAccounts#2019-06-01' = {
name: 'test'
location: 'East US'
sku: {
name: 'Standard_RAGRS'
}
kind: 'StorageV2'
properties: {
networkAcls: {
bypass: 'AzureServices'
virtualNetworkRules: []
ipRules: []
defaultAction: 'Allow'
}
supportsHttpsTrafficOnly: true
encryption: {
services: {
file: {
keyType: 'Account'
enabled: true
}
blob: {
keyType: 'Account'
enabled: true
}
}
keySource: 'Microsoft.Storage'
}
}
}
resource blobStorageService 'Microsoft.Storage/storageAccounts/blobServices#2019-06-01' = {
parent: storage_account_blob
name: 'default'
properties: {
lastAccessTimeTrackingPolicy: {
blobType: [
'string'
]
enable: true
name: 'AccessTimeTracking'
trackingGranularityInDays: 1
}
}
}
resource blobStorage_container_input 'Microsoft.Storage/storageAccounts/blobServices/containers#2019-06-01' = {
name: 'input'
properties: {
defaultEncryptionScope: '$account-encryption-key'
denyEncryptionScopeOverride: false
publicAccess: 'None'
}
parent: blobStorageService
}
resource management_policies 'Microsoft.Storage/storageAccounts/managementPolicies#2019-06-01' = {
name: 'default'
properties: {
policy:{
rules: [
{
definition:{
actions:{
baseBlob:{
delete:{
daysAfterLastAccessTimeGreaterThan: 60
}
tierToArchive:{
daysAfterLastAccessTimeGreaterThan: 30
}
tierToCool:{
daysAfterLastAccessTimeGreaterThan:15
}
}
}
filters:{
blobTypes:[
'blockBlob'
]
}
}
enabled: true
name: 'testRules'
type: 'Lifecycle'
}
]
}
}
parent: storage_account_blob
}
Do I have to initially maybe create the storage account with the blob service prior to creating the life cycle management policies?
I'm not sure, but have you tried to set an "depends on" on the "resource management_policies" and point it on "resource blobStorageService".
I am currently trying to deploy out a resource group using azure bicep, however, I am running into an issue using key vault for my azure app service. I would like to know if I am actually doing this the correct way. I have a main bicep file that is along the lines of:
// params removed for brevity...
targetScope = 'subscription'
resource rg 'Microsoft.Resources/resourceGroups#2021-04-01' = {
name: 'rg-${appName}-${region}'
location: 'centralus'
}
module appServicePlan 'appplan.bicep' = {
params: {
sku: appServicePlanSku
appName: appName
region: region
}
scope: rg
name: 'AppServicePlanDeploy'
}
module keyVault 'keyvault.bicep' = {
params: {
keyVaultName: keyVaultName
sqlPassword: sqlServerPassword
webSiteManagedId: webSite.outputs.webAppPrincipal
}
scope: rg
name: 'KeyVaultDeploy'
dependsOn: [
webSite
]
}
module ai 'ai.bicep' = {
scope: rg
name: 'ApplicationInsightsDeploy'
params: {
name: appName
region: region
keyVaultName: keyVault.outputs.keyVaultName
}
dependsOn: [
keyVault
]
}
resource kv 'Microsoft.KeyVault/vaults#2019-09-01' existing = {
name: keyVaultName
scope: rg
}
module sql 'sqlserver.bicep' = {
scope: rg
name: 'SQLServerDeploy'
params: {
appName: appName
region: region
sqlPassword: kv.getSecret('sqlPassword')
sqlCapacitity: sqlCapacitity
sqlSku: sqlSku
sqlTier: sqlTier
}
dependsOn: [
keyVault
]
}
module webSite 'site.bicep' = {
params: {
appName: appName
region: region
keyVaultName: keyVaultName
serverFarmId: appServicePlan.outputs.appServicePlanId
}
scope: rg
name: 'AppServiceDeploy'
dependsOn: [
appServicePlan
]
}
My question comes with the implementation of the site.bicep, I started off by passing the secret uri from exported variables and creating the web app last as app insights, sql, etc... all need to be setup and in keyvault before we use their exported secret uri to construct a config. I had something along the lines of:
site.bicep (before):
properties: {
serverFarmId: serverFarmId
keyVaultReferenceIdentity: userAssignedId
siteConfig: {
appSettings: [
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: '#Microsoft.KeyVault(SecretUri=${appInsightsConnectionString})'
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: '#Microsoft.KeyVault(SecretUri=${appInsightsKey})'
}
]
netFrameworkVersion: 'v5.0'
}
}
The only problem with this implementation is that the key vault MUST be constructed before the website because sql, ai, and the other services will store their values inside of the key vault for the web app to consume by their respective uris. The issue with this is that the KeyVault rightfully so has no idea which azure service to let access it's keys.
My question is the solution of constructing the web app before the key vault the only way to beat this problem? I am using managed identities on the web app and would like to continue doing so if possible. My final solution ended up somewhat like this:
site.bicep (final)
// params removed for brevity...
resource webApplication 'Microsoft.Web/sites#2020-12-01' = {
name: 'app-${appName}-${region}'
location: resourceGroup().location
tags: {
'hidden-related:${resourceGroup().id}/providers/Microsoft.Web/serverfarms/appServicePlan': 'Resource'
}
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: serverFarmId
siteConfig: {
appSettings: [
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: '#Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiConnectionString)'
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: '#Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiInstrumentationKey)'
}
{
name: 'AngularConfig:ApplicationInsightsKey'
value: '#Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiInstrumentationKey)'
}
]
netFrameworkVersion: 'v5.0'
}
}
}
output webAppPrincipal string = webApplication.identity.principalId
And the KeyVault which will take a dependsOn webSite
keyVault.bicep(final):
resource keyVault 'Microsoft.KeyVault/vaults#2019-09-01' = {
name: keyVaultName
location: resourceGroup().location
properties: {
enabledForDeployment: true
enabledForTemplateDeployment: true
enabledForDiskEncryption: true
enableRbacAuthorization: true
tenantId: subscription().tenantId
sku: {
name: 'standard'
family: 'A'
}
accessPolicies: [
{
tenantId: subscription().tenantId
objectId: webSiteManagedId
permissions: {
keys: [
'get'
]
secrets: [
'list'
'get'
]
}
}
]
}
}
Just treat your accessPolicies as separate resource and add them when both Key Vault and App Service are created. Same applies for Config section and Connection Strings. Check documentation here.
In ARM templates you can achieve same effect using nested templates. In Bicep it is kind the same, but you declare them as separate resource that usually contains parent name (e.g. name: '${kv.name}/add', name: '${webSite.name}/connectionstrings')
Sample
Step 1: Create an App Service without config section
resource webSite 'Microsoft.Web/sites#2020-12-01' = {
name: webSiteName
location: location
properties: {
serverFarmId: hostingPlan.id
siteConfig:{
netFrameworkVersion: 'v5.0'
}
}
identity: {
type:'SystemAssigned'
}
}
Step 2: Create Key Vault without access policies
resource kv 'Microsoft.KeyVault/vaults#2019-09-01' = {
name: keyVaultName
location: location
properties:{
sku:{
family: 'A'
name: 'standard'
}
tenantId: tenantId
enabledForTemplateDeployment: true
accessPolicies:[
]
}
}
Step 3: Create new access policy and reference Web Apps Managed Identity
resource keyVaultAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies#2021-06-01-preview' = {
name: '${kv.name}/add'
properties: {
accessPolicies: [
{
tenantId: tenantId
objectId: webSite.identity.principalId
permissions: {
keys: [
'get'
]
secrets: [
'list'
'get'
]
}
}
]
}
}
Step 4: Update Webb app config section
resource webSiteConnectionStrings 'Microsoft.Web/sites/config#2020-06-01' = {
name: '${webSite.name}/connectionstrings'
properties: {
DefaultConnection: {
value: '#Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiConnectionString)'
type: 'SQLAzure'
}
}
}
One solution could be to use User Assigend Identity instead of System Assigned. Then you would deploy the following:
Deploy a user assigend identity
Key Vault and assign permissions for user assigned identity
Deploy web app with user assigned identity and read / write secrets
User assigned is independent of the resources and so you avoid your chicken and egg problem.
More:
https://learn.microsoft.com/en-us/azure/templates/microsoft.managedidentity/userassignedidentities?tabs=bicep
https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
You should split up three parts of your deployment into separate resources:
Deploy the Key Vault - without any access policies!
Deploy the App Service - with the SystemAssigned Identity, but without the app settings
Deploy the Key Vault Access Policy for the MSI
Deploy the App Settings
I am trying to implement Azure Front Door Premium with a Web Application Firewall connection. I am able to create the Front Door both manually and through Bicep. However, when I try to connect to a WAF through Bicep, I'm not sure if it completely works.
The Bicep resource for my WAF looks like:
resource profiles_gbt_nprod_sandbox_FrontDoorTest_name_AzureFDTest_ac196269 'Microsoft.Cdn/profiles/securitypolicies#2020-09-01' = {
parent: profiles_gbt_nprod_sandbox_FrontDoorTest_name_resource
name: 'AzureFDTest-ac196269'
properties: {
parameters: {
wafPolicy: {
id: frontdoorwebapplicationfirewallpolicies_AzureFDTest_externalid
}
associations: [
{
domains: [
{
id: profiles_gbt_nprod_sandbox_FrontDoorTest_name_TestFDEndpoint.id
}
]
patternsToMatch: [
'/*'
]
}
]
type: 'WebApplicationFirewall'
}
}
}
To get: AzureFDTest-ac196269 I created the Front Door through Bicep, then manually connected the AzureFDTest policy and it generated this name.
When this is run, it looks like it connects to my Front Door in the Endpoint Manager:
But when I click on the AzureFDTest WAF policy it looks like:
And AzureFDTest is not listed. If I was to manually connect the WAF, this drop down menu would say AzureFDTest. Is this still working as expected or is there an issue with the way I have the resource written?
You can connect an Azure Front Door Premium to a WAF in Bicep via a security policy as follows:
var frontdoorName = 'frontDoor'
var frontDoorSkuName = 'Premium_AzureFrontDoor'
var endpointName = 'endpoint'
var wafPolicyName = 'wafPolicy'
var securityPolicyName = 'securityPolicy'
param tags object
// Front Door CDN profile
resource profile 'Microsoft.Cdn/profiles#2020-09-01' = {
name: frontdoorName
location: 'global'
sku: {
name: frontDoorSkuName
}
tags: tags
}
// Azure Front Door endpoint
resource endpoint 'Microsoft.Cdn/profiles/afdEndpoints#2020-09-01' = {
parent: profile
name: endpointName
location: 'Global'
tags: tags
properties: {
originResponseTimeoutSeconds: 60
enabledState: 'Enabled'
}
}
// WAF policy using Azure managed rule sets
resource wafPolicy 'Microsoft.Network/FrontDoorWebApplicationFirewallPolicies#2020-11-01' = {
name: wafPolicyName
location: 'global'
tags: tags
sku: {
name: frontDoorSkuName
}
properties: {
policySettings: {
enabledState: 'Enabled'
mode: 'Prevention'
}
managedRules: {
managedRuleSets: [
{
ruleSetType: 'Microsoft_DefaultRuleSet'
ruleSetVersion: '1.1'
}
{
ruleSetType: 'Microsoft_BotManagerRuleSet'
ruleSetVersion: '1.0'
}
]
}
}
}
// Security policy for Front Door which defines the WAF policy linking
resource securityPolicy 'Microsoft.Cdn/profiles/securityPolicies#2020-09-01' = {
parent: profile
name: securityPolicyName
properties: {
parameters: {
type: 'WebApplicationFirewall'
wafPolicy: {
id: wafPolicy.id
}
associations: [
{
domains: [
{
id: endpoint.id
}
]
patternsToMatch: [
'/*'
]
}
]
}
}
}
There is also an azure-quickstart-template available for this scenario:
Front Door Premium with Web Application Firewall and Microsoft-managed rule sets