Bicep - Deploy Azure Firewall into Virtual Network
Hey there! In another post, we discussed how you can use Terraform to deploy Azure Firewall and how it can make your cloud deployments more efficient and secure. Today, let’s explore how you can achieve similar results using Bicep, Azure’s language for resource declaration.
Why Bicep? #
Bicep is a language developed by Microsoft to make it easier to deploy Azure resources. It simplifies creating IaC scripts by using a cleaner syntax than ARM templates. Bicep scripts are easier to read and maintain. Azure Firewall is a fully managed cloud-native network firewall service that can be deployed effectively using Bicep to protect your Azure virtual network resources.
Azure Firewall #
Azure Firewall is a security tool that protects your Azure Virtual Network resources. It is a firewall service that checks the state of your network traffic. Azure Firewall is designed to be highly available and scalable. It has features like application rule collections, network rule collections, and threat intelligence.
Let’s get started and see how this deployment can be done.
Prerequisites #
Before you start, you’ll need the following to deploy and manage resources with Bicep:
- You need Azure CLI version 2.20.0 or later to deploy Bicep files on your local machine.
- A text editor or IDE of your choice (Visual Studio Code with Bicep extension is my recommendation)
Create the Bicep files #
The first step in deploying a Bicep template is to create the Bicep file that defines your resources. Create a new file named afw.bicep. This file will contain the code needed to define and configure the deployment of your resources.
@description('Name of the project or solution. Must be between 3 and 18 characters.')
@minLength(3)
@maxLength(18)
param projectName string
@description('The prefix for the Azure Firewall name.')
param firewallPrefix string = 'afw'
@description('The prefix for the Public IP name.')
param publicIPPrefix string = 'pip'
@description('Deployment Location')
@allowed([
'westeurope'
'northeurope'
'uksouth'
])
param location string
@description('The SKU name of the Azure Firewall.')
@allowed([
'AZFW_VNet'
'AZFW_Hub'
])
param skuName string = 'AZFW_VNet'
@description('The SKU tier of the Azure Firewall.')
@allowed([
'Standard'
'Premium'
])
param skuTier string = 'Standard'
@description('The zones of the Azure Firewall.')
param zones array = [ '1' ]
@description('The allocation method of the public IP address for the Azure Firewall.')
@allowed([
'Static'
'Dynamic'
])
param publicIPAllocationMethod string = 'Static'
@description('The IP version of the public IP address for the Azure Firewall.')
@allowed([
'IPv4'
'IPv6'
])
param publicIPAddressVersion string = 'IPv4'
@description('The name of the virtual network for the Azure Firewall.')
param vnetName string
@description('The name of the subnet for the Azure Firewall. This must be named "AzureFirewallSubnet".')
param subnetName string = 'AzureFirewallSubnet'
@description('The prefix for the subnet to be created. This must have a size of at least /26')
param subnetPrefix string
@description('List of service endpoints to be associated with the subnet.')
param serviceEndpoints array = []
@description('The tags to be associated with the resources.')
param tags object = {
bicep: 'true'
environment: 'jorgebernhatdt.com'
}
// Use string interpolation to combine the prefix and name
var firewallName = '${firewallPrefix}-${projectName}'
var publicIPName = '${publicIPPrefix}-${projectName}'
// Public IP address resource for Azure Firewall
resource publicIP 'Microsoft.Network/publicIPAddresses@2023-06-01' = {
name: publicIPName
location: location
tags: tags
sku: {
name: 'Standard'
}
zones: zones
properties: {
publicIPAllocationMethod: publicIPAllocationMethod
publicIPAddressVersion: publicIPAddressVersion
}
}
// Subnet resource to configure the subnet address range and associated service endpoints.
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-06-01' = {
name: '${vnetName}/${subnetName}'
properties: {
addressPrefix: subnetPrefix
serviceEndpoints: serviceEndpoints
delegations: []
}
}
// Azure Firewall resource
resource firewall 'Microsoft.Network/azureFirewalls@2023-06-01' = {
name: firewallName
location: location
tags: tags
properties: {
sku: {
name: skuName
tier: skuTier
}
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
publicIPAddress: {
id: publicIP.id
}
subnet: {
id: subnet.id
}
}
}
]
}
zones: zones
}
// Output the name and public IP address of the firewall
@description('The resource ID of the Azure Firewall.')
output firewallResourceId string = firewall.id
@description('The public IP address of the Azure Firewall.')
output publicIPAddress string = publicIP.properties.ipAddress
publicIP: This resource block defines the public IP address for the Azure Firewall, including its name, location, SKU, zones, allocation method, and IP version.
subnet: Specifies the subnet within the virtual network, including its name, address prefix, and any service endpoints.
firewall: The main Azure Firewall resource definition, including its name, location, SKU, IP configurations (linking to the public IP and subnet), and tags.
Deployment scope #
You can target your deployment to a resource group, subscription, management group, or tenant. In this case, when deploying Azure Firewall, an Azure resource group is necessary to organize all related resources. By default, when deploying a Bicep template, the scope where the resource should be deployed is a resource group.
You can use an existing Resource Group, or you can create a new Resource Group. If you want to know how to create a Resource Group using Azure CLI, check out this link.
Deploy the Bicep template using the Azure CLI #
Once your Bicep template is prepared, and you’ve selected your desired scope, you can proceed to deploy the template through the Azure CLI. To do so, execute the following commands.
Parameters #
Personalization is key to making your template reusable. With the parameters, you can easily tailor the template to your specific needs. You can use either inline parameters or a parameter file to pass parameter values. In my case, I will use a file to pass the parameters; here is an example.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"projectName": {
"value": "demo"
},
"location": {
"value": "uksouth"
},
"skuName": {
"value": "AZFW_VNet"
},
"skuTier": {
"value": "Standard"
},
"zones": {
"value": ["1"]
},
"publicIPAllocationMethod": {
"value": "Static"
},
"publicIPAddressVersion": {
"value": "IPv4"
},
"vnetName": {
"value": "VNET-HUB"
},
"subnetPrefix": {
"value": "10.0.1.0/26"
},
"serviceEndpoints": {
"value": [
{"service": "Microsoft.Storage"},
{"service": "Microsoft.Sql"},
{"service": "Microsoft.KeyVault"},
{"service": "Microsoft.ServiceBus"}
]
},
"tags": {
"value": {
"bicep": "true",
"environment": "jorgebernhardt.com"
}
}
}
}
Important: Please note that the parameter file stores parameter values in plain text format. If you need to include a parameter with sensitive data, it’s recommended to store the value in a secure key vault.
Preview changes #
Before deploying a Bicep file, you can preview the changes that will occur to your resources. Using what-if operations does not change existing resources; it simply shows you an output that includes color-coded results that allow you to see different changes.
az deployment group what-if \
--resource-group <resource-group-name> \
--template-file <filename>.bicep \
--parameters @<filename>.parameters.json
Deploy the Azure resource #
Finally, to deploy the template, run the following command.
az deployment group create \
--resource-group <resource-group-name> \
--template-file <filename>.bicep \
--parameters @<filename>.parameters.json
Validate the deployment #
To verify that the resource was created correctly, you can either use the Azure Portal or the Azure CLI to check the created resources and their configurations. For Azure CLI, use the following command.
az network firewall show \
--name <firewall-name> \
--resource-group <resource-group-name> \
--query "{Name:name, Location:location, SKU:sku.name, Tier:sku.tier}" \
--output table
References and useful links #
Thank you for taking the time to read my post. I sincerely hope that you find it helpful.