Quickstart: Create an Azure Web Application Firewall v2 by using an ARM template
In this quickstart, you use an Azure Resource Manager template (ARM template) to create an Azure Web Application Firewall (WAF) v2 on Azure Application Gateway.
An Azure Resource Manager template is a JavaScript Object Notation (JSON) file that defines the infrastructure and configuration for your project. The template uses declarative syntax. You describe your intended deployment without writing the sequence of programming commands to create the deployment.
Note
We recommend that you use the Azure Az PowerShell module to interact with Azure. To get started, see Install Azure PowerShell. To learn how to migrate to the Az PowerShell module, see Migrate Azure PowerShell from AzureRM to Az.
If your environment meets the prerequisites and you're familiar with using ARM templates, you can select the Deploy to Azure button to open the template in the Azure portal.
Prerequisites
- An Azure account with an active subscription. If you don't have one, you can create an account for free.
Review the template
This template creates a simple Web Application Firewall v2 on Azure Application Gateway. The template creates a public IP frontend IP address, HTTP settings, a rule with a basic listener on port 80, and a backend pool. A WAF policy with a custom rule blocks traffic to the backend pool based on an IP address match type.
The template defines the following Azure resources:
- Microsoft.Network/applicationgateways
- Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies
- Microsoft.Network/publicIPAddresses, one for the application gateway and two for the virtual machines (VMs)
- Microsoft.Network/networkSecurityGroups
- Microsoft.Network/virtualNetworks
- Microsoft.Compute/virtualMachines, two VMs
- Microsoft.Network/networkInterfaces, one for each VM
- Microsoft.Compute/virtualMachine/extensions to configure IIS and the web pages
This template is from Azure Quickstart Templates.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.15.31.15270",
"templateHash": "7253194970749033988"
}
},
"parameters": {
"adminUsername": {
"type": "string",
"metadata": {
"description": "Admin username for the backend servers"
}
},
"adminPassword": {
"type": "securestring",
"metadata": {
"description": "Password for the admin account on the backend servers"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"vmSize": {
"type": "string",
"defaultValue": "Standard_B2ms",
"metadata": {
"description": "Size of the virtual machine."
}
}
},
"variables": {
"virtualMachines_myVM_name": "myVM",
"virtualNetworks_myVNet_name": "myVNet",
"myNic_name": "net-int",
"ipconfig_name": "ipconfig",
"publicIPAddress_name": "public_ip",
"nsg_name": "vm-nsg",
"applicationGateways_myAppGateway_name": "myAppGateway",
"vnet_prefix": "10.0.0.0/16",
"ag_subnet_prefix": "10.0.0.0/24",
"backend_subnet_prefix": "10.0.1.0/24",
"AppGW_AppFW_Pol_name": "WafPol01"
},
"resources": [
{
"copy": {
"name": "nsg",
"count": "[length(range(0, 2))]"
},
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2021-08-01",
"name": "[format('{0}{1}', variables('nsg_name'), add(range(0, 2)[copyIndex()], 1))]",
"location": "[parameters('location')]",
"properties": {
"securityRules": [
{
"name": "RDP",
"properties": {
"protocol": "Tcp",
"sourcePortRange": "*",
"destinationPortRange": "3389",
"sourceAddressPrefix": "*",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 300,
"direction": "Inbound"
}
}
]
}
},
{
"copy": {
"name": "publicIPAddress",
"count": "[length(range(0, 3))]"
},
"type": "Microsoft.Network/publicIPAddresses",
"apiVersion": "2021-08-01",
"name": "[format('{0}{1}', variables('publicIPAddress_name'), range(0, 3)[copyIndex()])]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard"
},
"properties": {
"publicIPAddressVersion": "IPv4",
"publicIPAllocationMethod": "Static",
"idleTimeoutInMinutes": 4
}
},
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2021-08-01",
"name": "[variables('virtualNetworks_myVNet_name')]",
"location": "[parameters('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('vnet_prefix')]"
]
},
"subnets": [
{
"name": "myAGSubnet",
"properties": {
"addressPrefix": "[variables('ag_subnet_prefix')]",
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
},
{
"name": "myBackendSubnet",
"properties": {
"addressPrefix": "[variables('backend_subnet_prefix')]",
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
}
],
"enableDdosProtection": false,
"enableVmProtection": false
}
},
{
"copy": {
"name": "myVM",
"count": "[length(range(0, 2))]"
},
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2021-11-01",
"name": "[format('{0}{1}', variables('virtualMachines_myVM_name'), add(range(0, 2)[copyIndex()], 1))]",
"location": "[parameters('location')]",
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"storageProfile": {
"imageReference": {
"publisher": "MicrosoftWindowsServer",
"offer": "WindowsServer",
"sku": "2019-Datacenter",
"version": "latest"
},
"osDisk": {
"osType": "Windows",
"createOption": "FromImage",
"caching": "ReadWrite",
"managedDisk": {
"storageAccountType": "StandardSSD_LRS"
},
"diskSizeGB": 127
}
},
"osProfile": {
"computerName": "[format('{0}{1}', variables('virtualMachines_myVM_name'), add(range(0, 2)[copyIndex()], 1))]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]",
"windowsConfiguration": {
"provisionVMAgent": true,
"enableAutomaticUpdates": true
},
"allowExtensionOperations": true
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', format('{0}{1}', variables('myNic_name'), add(range(0, 2)[copyIndex()], 1)))]"
}
]
}
},
"dependsOn": [
"myNic"
]
},
{
"copy": {
"name": "myVM_IIS",
"count": "[length(range(0, 2))]"
},
"type": "Microsoft.Compute/virtualMachines/extensions",
"apiVersion": "2021-11-01",
"name": "[format('{0}{1}/IIS', variables('virtualMachines_myVM_name'), add(range(0, 2)[copyIndex()], 1))]",
"location": "[parameters('location')]",
"properties": {
"autoUpgradeMinorVersion": true,
"publisher": "Microsoft.Compute",
"type": "CustomScriptExtension",
"typeHandlerVersion": "1.4",
"settings": {
"commandToExecute": "powershell Add-WindowsFeature Web-Server; powershell Add-Content -Path \"C:\\inetpub\\wwwroot\\Default.htm\" -Value $($env:computername)"
}
},
"dependsOn": [
"myVM"
]
},
{
"type": "Microsoft.Network/applicationGateways",
"apiVersion": "2021-08-01",
"name": "[variables('applicationGateways_myAppGateway_name')]",
"location": "[parameters('location')]",
"properties": {
"sku": {
"name": "WAF_v2",
"tier": "WAF_v2",
"capacity": 2
},
"gatewayIPConfigurations": [
{
"name": "appGatewayIpConfig",
"properties": {
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworks_myVNet_name'), 'myAGSubnet')]"
}
}
}
],
"frontendIPConfigurations": [
{
"name": "appGwPublicFrontendIp",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', format('{0}0', variables('publicIPAddress_name')))]"
}
}
}
],
"frontendPorts": [
{
"name": "port_80",
"properties": {
"port": 80
}
}
],
"backendAddressPools": [
{
"name": "myBackendPool",
"properties": {}
}
],
"backendHttpSettingsCollection": [
{
"name": "myHTTPSetting",
"properties": {
"port": 80,
"protocol": "Http",
"cookieBasedAffinity": "Disabled",
"pickHostNameFromBackendAddress": false,
"requestTimeout": 20
}
}
],
"httpListeners": [
{
"name": "myListener",
"properties": {
"firewallPolicy": {
"id": "[resourceId('Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies', variables('AppGW_AppFW_Pol_name'))]"
},
"frontendIPConfiguration": {
"id": "[resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', variables('applicationGateways_myAppGateway_name'), 'appGwPublicFrontendIp')]"
},
"frontendPort": {
"id": "[resourceId('Microsoft.Network/applicationGateways/frontendPorts', variables('applicationGateways_myAppGateway_name'), 'port_80')]"
},
"protocol": "Http",
"requireServerNameIndication": false
}
}
],
"requestRoutingRules": [
{
"name": "myRoutingRule",
"properties": {
"ruleType": "Basic",
"priority": 10,
"httpListener": {
"id": "[resourceId('Microsoft.Network/applicationGateways/httpListeners', variables('applicationGateways_myAppGateway_name'), 'myListener')]"
},
"backendAddressPool": {
"id": "[resourceId('Microsoft.Network/applicationGateways/backendAddressPools', variables('applicationGateways_myAppGateway_name'), 'myBackendPool')]"
},
"backendHttpSettings": {
"id": "[resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', variables('applicationGateways_myAppGateway_name'), 'myHTTPSetting')]"
}
}
}
],
"enableHttp2": false,
"firewallPolicy": {
"id": "[resourceId('Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies', variables('AppGW_AppFW_Pol_name'))]"
}
},
"dependsOn": [
"[resourceId('Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies', variables('AppGW_AppFW_Pol_name'))]",
"[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworks_myVNet_name'))]",
"publicIPAddress"
]
},
{
"type": "Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies",
"apiVersion": "2021-08-01",
"name": "[variables('AppGW_AppFW_Pol_name')]",
"location": "[parameters('location')]",
"properties": {
"customRules": [
{
"name": "CustRule01",
"priority": 100,
"ruleType": "MatchRule",
"action": "Block",
"matchConditions": [
{
"matchVariables": [
{
"variableName": "RemoteAddr"
}
],
"operator": "IPMatch",
"negationConditon": true,
"matchValues": [
"10.10.10.0/24"
]
}
]
}
],
"policySettings": {
"requestBodyCheck": true,
"maxRequestBodySizeInKb": 128,
"fileUploadLimitInMb": 100,
"state": "Enabled",
"mode": "Prevention"
},
"managedRules": {
"managedRuleSets": [
{
"ruleSetType": "OWASP",
"ruleSetVersion": "3.1"
}
]
}
}
},
{
"copy": {
"name": "myNic",
"count": "[length(range(0, 2))]"
},
"type": "Microsoft.Network/networkInterfaces",
"apiVersion": "2021-08-01",
"name": "[format('{0}{1}', variables('myNic_name'), add(range(0, 2)[copyIndex()], 1))]",
"location": "[parameters('location')]",
"properties": {
"ipConfigurations": [
{
"name": "[format('{0}{1}', variables('ipconfig_name'), add(range(0, 2)[copyIndex()], 1))]",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', format('{0}{1}', variables('publicIPAddress_name'), add(range(0, 2)[copyIndex()], 1)))]"
},
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworks_myVNet_name'), 'myBackendSubnet')]"
},
"primary": true,
"privateIPAddressVersion": "IPv4",
"applicationGatewayBackendAddressPools": [
{
"id": "[resourceId('Microsoft.Network/applicationGateways/backendAddressPools', variables('applicationGateways_myAppGateway_name'), 'myBackendPool')]"
}
]
}
}
],
"enableAcceleratedNetworking": false,
"enableIPForwarding": false,
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}{1}', variables('nsg_name'), add(range(0, 2)[copyIndex()], 1)))]"
}
},
"dependsOn": [
"[resourceId('Microsoft.Network/applicationGateways', variables('applicationGateways_myAppGateway_name'))]",
"[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworks_myVNet_name'))]",
"nsg",
"publicIPAddress"
]
}
]
}
Deploy the template
Deploy the ARM template to Azure:
Select Deploy to Azure to sign in to Azure and open the template. The template creates an application gateway, the network infrastructure, and two VMs in the backend pool running IIS.
Select or create a resource group.
Select Review + create, and when validation passes, select Create. The deployment can take 10 minutes or longer to complete.
Validate the deployment
Although IIS isn't required, the template installs IIS on the backend servers so you can verify that Azure successfully created a WAF v2 on the application gateway.
Use IIS to test the application gateway:
Copy the public IP address for the application gateway from its Overview page.
You can also search for application gateways in the Azure search box. The list of application gateways shows the public IP addresses in the Public IP address column.
Paste the IP address into the address bar of your browser to browse that address.
Check the response. A 403 Forbidden response verifies that the WAF is successfully blocking connections to the backend pool.
To change the custom rule to allow traffic, run the following Azure PowerShell script, replacing your resource group name:
$rg = "<your resource group name>" $AppGW = Get-AzApplicationGateway -Name myAppGateway -ResourceGroupName $rg $pol = Get-AzApplicationGatewayFirewallPolicy -Name WafPol01 -ResourceGroupName $rg $pol[0].customrules[0].action = "allow" $rule = $pol.CustomRules Set-AzApplicationGatewayFirewallPolicy -Name WafPol01 -ResourceGroupName $rg -CustomRule $rule $AppGW.FirewallPolicy = $pol Set-AzApplicationGateway -ApplicationGateway $AppGW
Refresh your browser several times. You should see connections to both myVM1 and myVM2.
Clean up resources
When you no longer need the resources you created in this quickstart, delete the resource group to remove the application gateway and all its related resources.
To delete the resource group, call the Remove-AzResourceGroup
cmdlet:
Remove-AzResourceGroup -Name "<your resource group name>"