Olivier Miossec
Posted on October 31, 2024
Modularity is a key when designing and coding Infrastructure as Code. You need to generalize and be flexible to reuse the code with different types of environments and situations.
Imagine a situation where you need to deploy resources in different Azure regions and environments (prod and dev). Each region and each environment has its specificities, some resources can be deployed in one but not the other, and some properties can change between regions or environments.
Let’s take a simple but concrete case: you need to deploy a VNET with two subnets. But in a production environment, you want to have a NAT Gateway, and in North Europe and France Central, one of the subnets should be private. Nat Gateway and private subnet aren't needed in dev, but you still need to deploy two subnets.
Creating a Bicep file deploying a Nat Gateway with its public IP, a VNET, and two subnets with the NAT Gateway ID, one of which is a private subnet (defaultOutboundAccess: false in Bicep).
But how can we deploy or not a resource based on one parameter?
Bicep has two options.
The first one is conditional deployment. You must add an if and an expression before the resource definition to use it.
resource logicalName 'resource@2023-07-01' = if (condition) {
xxxxxx
}
If the expression “Condition” is true, Bicep will deploy the resource, if not the resource is not deployed.
Here, we must deploy a Nat Gateway if prod environments only exist in North Europe and France Central.
The condition looks like
param environment string
environment == 'prod' && (location == 'francecentral' || location == 'northeurope')
We will need to use the condition on several resources, so it is wise to put it in a variable. It simplifies the code, especially here, with the complex evaluation using several logical operators see
var natGateWayDeployBool = environment == 'prod' && (location == 'francecentral' || location == 'northeurope')
Resources can be deployed using:
var natGateWayDeployBool = environment == 'prod' && (location == 'francecentral' || location == 'northeurope')
resource publicip 'Microsoft.Network/publicIPAddresses@2024-01-01' = if (natGateWayDeployBool) {
name: publicipname
location: location
sku: {
name: 'Standard'
}
properties: {
publicIPAddressVersion: 'IPv4'
publicIPAllocationMethod: 'Static'
idleTimeoutInMinutes: 4
}
}
resource natgateway 'Microsoft.Network/natGateways@2024-01-01' = if (natGateWayDeployBool) {
name: natGatewayName
location: location
sku: {
name: 'Standard'
}
properties: {
idleTimeoutInMinutes: 4
publicIpAddresses: [
{
id: publicip.id
}
]
}
}
The Nat Gateway and its Public IP are deployed only if the environment is prod and the location is North Europe and France Central.
Now, how to deal with subnets?
In production, in North Europe and France Central subnet looks like this:
resource subnet1 'Microsoft.Network/virtualNetworks/subnets@2024-03-01' = {
parent: vnet
name: subnet1Name
properties: {
addressPrefix: subnet1Prefix
natGateway: {
id: natgateway.id
}
}
}
resource subnet2 'Microsoft.Network/virtualNetworks/subnets@2024-03-01' = {
parent: vnet
name: subnet2Name
properties: {
addressPrefix: subnet2Prefix
defaultOutboundAccess: false
natGateway: {
id: natgateway.id
}
}
}
But for production in West Europe, we should remove the natGateway reference. For the dev environment, we should not have the natGatteway reference, and the defaultOutboundAccess property should be true.
First, let’s create a variable for the defaultOutboundAccess property it should be false for production and true for dev. The value should be false for the dev environment so we could use an ! (logical NOT in Bicep)
var defaultOutboundAccessBool = !(environment == 'prod')
The second subnet looks like:
resource subnet2 'Microsoft.Network/virtualNetworks/subnets@2024-03-01' = {
parent: vnet
name: subnet2Name
properties: {
addressPrefix: subnet2Prefix
defaultOutboundAccess: defaultOutboundAccessBool
natGateway: {
id: natgateway.id
}
}
}
We also need to remove the NatGateway Reference for dev and West Europe. The natGateWayDeployBool will be useful here.
If expression is not available inside a resource, only at the resource level, only the conditional expression ?: is permitted.
The form is simple
condition ? true-value : false-value
The natGateway property isn't a simple string, it asks for an expression, but you can use an expression with ?:
natGateway: (natGateWayDeployBool ? { id: natgateway.id } : null)
The subnet resources:
resource subnet1 'Microsoft.Network/virtualNetworks/subnets@2024-01-01' = {
parent: vnet
name: subnet1Name
properties: {
addressPrefix: subnet1Prefix
natGateway: (natGateWayDeployBool ? { id: natgateway.id } : null)
}
}
resource subnet2 'Microsoft. Network/virtualNetworks/subnets@2024-01-01' = {
parent: vnet
name: subnet2Name
properties: {
addressPrefix: subnet2Prefix
defaultOutboundAccess: defaultOutboundAccessBool
natGateway: (natGateWayDeployBool ? { id: natgateway.id } : null)
}
}
This is a simple example of what you can do with conditional deployment in Azure Bicep.
The full bicep code
param location string = resourceGroup().location
@description('VNet name')
param vnetName string
@description('Vnet Address space')
param vnetAddressPrefix string
@description('Subnet1 Prefix')
param subnet1Prefix string
@description('Subnet1 Name')
param subnet1Name string
@description('Subnet2 Prefix')
param subnet2Prefix string
@description('Subnet2 Name')
param subnet2Name string
@description('Environment Type')
@allowed([
'dev'
'prod'
])
param environment string
@description('Public IP Nqme')
param publicipname string = 'pibIPNqt'
@description('Nat Gateway Name')
param natGatewayName string = 'NattGw'
var natGateWayDeployBool = environment == 'prod' && (location == 'francecentral' || location == 'northeurope')
var defaultOutboundAccessBool = !(environment == 'prod')
resource publicip 'Microsoft.Network/publicIPAddresses@2024-01-01' = if (natGateWayDeployBool) {
name: publicipname
location: location
sku: {
name: 'Standard'
}
properties: {
publicIPAddressVersion: 'IPv4'
publicIPAllocationMethod: 'Static'
idleTimeoutInMinutes: 4
}
}
resource natgateway 'Microsoft.Network/natGateways@2024-01-01' = if (natGateWayDeployBool) {
name: natGatewayName
location: location
sku: {
name: 'Standard'
}
properties: {
idleTimeoutInMinutes: 4
publicIpAddresses: [
{
id: publicip.id
}
]
}
}
resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [
vnetAddressPrefix
]
}
}
}
resource subnet1 'Microsoft.Network/virtualNetworks/subnets@2024-01-01' = {
parent: vnet
name: subnet1Name
properties: {
addressPrefix: subnet1Prefix
natGateway: (natGateWayDeployBool ? { id: natgateway.id } : null)
}
}
resource subnet2 'Microsoft.Network/virtualNetworks/subnets@2024-01-01' = {
parent: vnet
name: subnet2Name
properties: {
addressPrefix: subnet2Prefix
defaultOutboundAccess: defaultOutboundAccessBool
natGateway: (natGateWayDeployBool ? { id: natgateway.id } : null)
}
}
Posted on October 31, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.