“Wait… now we need networks in every country, plus a list of server URLs for the devs?”
Me: Sounds like a job for Bicep loops. 🛠️😎
💡 Scenario
We’re expanding the smart teddy bear app to multiple countries. That means:
- We need a virtual network (VNet) in each location.
- Each VNet should have subnets for the frontend and backend.
- Devs also want a list of SQL server FQDNs after deployment so they can connect from anywhere.
Let’s break it down and automate it 💪
🚧 What You’ll Learn
Feature | Why It Matters |
---|---|
Variable Loops | Define repeating items like subnets dynamically |
Output Loops | Return info (like FQDNs) for each location |
Modular Deployment | Keeps things clean and scalable |
🧩 Step 1: Add Network Config to Your Bicep Template
Open main.bicep
.
➕ Add These Parameters:
@description('IP range for the virtual networks')
param virtualNetworkAddressPrefix string = '10.10.0.0/16'
@description('Subnets for each VNet')
param subnets array = [
{
name: 'frontend'
ipAddressRange: '10.10.5.0/24'
}
{
name: 'backend'
ipAddressRange: '10.10.10.0/24'
}
]
Think of it like defining rooms inside a house — one house (VNet) per location, each with two rooms (subnets): frontend and backend.
🔁 Step 2: Use a Variable Loop for Subnets
var subnetProperties = [for subnet in subnets: {
name: subnet.name
properties: {
addressPrefix: subnet.ipAddressRange
}
}]
This loop dynamically builds the list of subnets for each VNet based on our parameter. No more hardcoding!
🏡 Step 3: Deploy One VNet Per Location
Under your existing module databases
loop, add this:
resource virtualNetworks 'Microsoft.Network/virtualNetworks@2024-05-01' = [for location in locations: {
name: 'teddybear-${location}'
location: location
properties:{
addressSpace:{
addressPrefixes:[
virtualNetworkAddressPrefix
]
}
subnets: subnetProperties
}
}]
Every location in the locations
array gets its own teddy-themed VNet 🧸
📬 Step 4: Add Outputs to Your database.bicep
Module
Open modules/database.bicep
and at the bottom, add:
output serverName string = sqlServer.name
output location string = location
output serverFullyQualifiedDomainName string = sqlServer.properties.fullyQualifiedDomainName
These act like a return statement — giving us back key details about each SQL server.
📦 Step 5: Loop Over Outputs in Your Main Template
Now back in main.bicep
, at the bottom, add this:
output serverInfo array = [for i in range(0, length(locations)): {
name: databases[i].outputs.serverName
location: databases[i].outputs.location
fullyQualifiedDomainName: databases[i].outputs.serverFullyQualifiedDomainName
}]
You now get a full list of all deployed SQL server names, their regions, and their FQDNs. Your devs will love you for this 👨💻
🧪 Example: Final main.bicep Structure
Your main.bicep
will now contain:
- Secure login parameters
- Location list
- VNet and subnet settings
- Database module loop
- VNet loop
- Output loop
param locations array = [ 'westus', 'eastus2', 'eastasia' ]
param sqlServerAdministratorLogin string
param sqlServerAdministratorLoginPassword string
param virtualNetworkAddressPrefix string = '10.10.0.0/16'
param subnets array = [
{ name: 'frontend'; ipAddressRange: '10.10.5.0/24' }
{ name: 'backend'; ipAddressRange: '10.10.10.0/24' }
]
var subnetProperties = [for subnet in subnets: {
name: subnet.name
properties: {
addressPrefix: subnet.ipAddressRange
}
}]
module databases 'modules/database.bicep' = [for location in locations: {
name: 'database-${location}'
params: {
location: location
sqlServerAdministratorLogin: sqlServerAdministratorLogin
sqlServerAdministratorLoginPassword: sqlServerAdministratorLoginPassword
}
}]
resource virtualNetworks 'Microsoft.Network/virtualNetworks@2024-05-01' = [for location in locations: {
name: 'teddybear-${location}'
location: location
properties: {
addressSpace: {
addressPrefixes: [ virtualNetworkAddressPrefix ]
}
subnets: subnetProperties
}
}]
output serverInfo array = [for i in range(0, length(locations)): {
name: databases[i].outputs.serverName
location: databases[i].outputs.location
fullyQualifiedDomainName: databases[i].outputs.serverFullyQualifiedDomainName
}]
🚀 Deploy It!
Make sure you’re set up:
az bicep install && az bicep upgrade
az login
az group create --name BicepRG --location westus
Then deploy:
az deployment group create \
--resource-group BicepRG \
--name main \
--template-file main.bicep
💬 You’ll be prompted for the SQL login and password — make sure they’re secure and meet Azure’s password rules.
🔎 Post-Deployment: What to Check
Head to the Azure Portal:
- Open your Resource Group
- You should see three virtual networks (westus, eastus2, eastasia)
- Each VNet should have frontend and backend subnets
- Scroll to Deployments > main > Outputs
- You’ll see an array of your SQL server FQDNs, ready to be copied!
✅ Devs can now grab their connection strings. Ops teams are happy. Mission complete 🎯
🧠 In Short
Feature | What We Did |
---|---|
Parameterized Subnets | Passed in subnet structure as a parameter |
Variable Loop | Built reusable subnet configs |
Resource Loop | Deployed 1 VNet per region |
Output Loop | Gathered SQL server details for dev handoff |
Wanna follow my Azure learning journey?
Stick around — I’m sharing it all, wins and stumbles included 😄
You can find me on LinkedIn — drop me a message and just say hi 👋
Would love to hear what you're working on or learning!