I’ve been wanting to build something from scratch in Azure for a while — something structured, repeatable, and deployable across regions. I didn’t want just a “hello world” template. I wanted a multi-region setup, built with Bicep, that could handle App Services, SQL databases, VNets, and environment-based configs.
So I sat down, gave myself some rough requirements, and just started building.
📦 The Goal
- Deploy to multiple regions (
westus
,eastus2
,westus2
) - Use a modular Bicep structure for App Services, SQL DBs, and VNets
- Support
dev
andprod
environments (with some real differences) - Include outputs I could test and click straight away
- Keep secrets secure using
@secure()
params
🛠️ What It Deploys
Each region gets:
- An App Service + App Service Plan (F1 for dev, P1v2 for prod)
- A SQL Server + SQL Database (with auditing in prod)
- A Virtual Network with
frontend
andbackend
subnets - Dynamic naming using
uniqueString()
so I don’t fight name collisions - Easy-to-read summaries in the output
🔄 Modular Architecture
I split everything into separate Bicep modules:
Module | Purpose |
---|---|
app.bicep |
App Service + App Plan |
database.bicep |
SQL Server + DB + optional auditing |
vnet.bicep |
Virtual network + subnets per region |
In the main file, I loop through my locations
array and call each module with region-specific parameters:
module appModule 'modules/app.bicep' = [for (location, i) in locations: {
name: 'app-${location}'
params: {
location: location
...
}
}]
🧠 Some of the Errors I Ran Into (and Fixed)
🔁 Looping Too Late
Early on, I had only one VNet — then I realized, "Wait, this should be region-specific too." I refactored that into a loop with an index
param to dynamically set IP ranges like 10.${index}.0.0/16
.
❌ API Version Errors in Certain Regions
This one hit hard. Everything looked good locally. But when I deployed?
“No registered resource provider found for location ‘westus’ and API version ‘2023-09-01’ for type ‘servers’”
Turns out, not all API versions are available in all regions. I had picked a newer API version (2023-09-01
) that wasn’t supported everywhere — especially for Microsoft.Web/sites
, Microsoft.Sql/servers
, and Microsoft.Network/virtualNetworks
.
💡 The fix? I rolled back to known stable versions that are widely supported:
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { ... }
resource webApp 'Microsoft.Web/sites@2022-03-01' = { ... }
resource sqlServer 'Microsoft.Sql/servers@2021-11-01' = { ... }
resource sqlDb 'Microsoft.Sql/servers/databases@2021-11-01' = { ... }
resource sqlAudit 'Microsoft.Sql/servers/auditingSettings@2021-11-01' = { ... }
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { ... }
resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-05-01' = { ... }
After that — clean deployment. ✅
⚠️ Warnings (BCP081)
These still show up occasionally:
Warning BCP081: Resource type ... does not have types available. Bicep is unable to validate resource properties prior to deployment...
They don’t block deployment, but they’re a good reminder that the tooling isn’t perfect — and that I should always test the actual deployments.
🔐 Dev vs Prod Logic
I wanted to simulate real environments. Here’s what changes based on the environment:
Feature | Dev | Prod |
---|---|---|
App Plan SKU | F1 |
P1v2 |
SQL Auditing | Off | On |
Storage Account for logs | Skipped | Created |
var auditingEnabled = environment == 'prod'
This little flag is used throughout the modules to decide whether to deploy extra resources like storage for audit logs.
🚀 Outputs That Just Work
After every deployment, I get a summary like:
[
{
"region": "westus",
"url": "https://app-westus-xyz.azurewebsites.net"
},
...
]
That https://
prefix I added manually so I could just click and test right away. It’s the small stuff, right?
✅ End Result: A Multi-Region MVP
It took some hiccups (especially around API versions), but I ended up with:
- A reusable infrastructure setup
- Clean, modular Bicep code
- Working deployments across multiple Azure regions
- Environment logic I can extend in the future
I still have features I want to add (like Key Vault integration and CI/CD), but this feels like a solid MVP I can build on.
🧪 Final Thoughts
This project reminded me that getting something real deployed is the best way to learn. You don’t just memorize syntax — you hit actual infrastructure challenges: mismatched API versions, region-specific quirks, naming collisions, and environment-based logic.
And solving those? That’s where the real fun (and growth) is.
If you’re curious to dig into the code, or you're working on your own Bicep project, feel free to check out the full repo here:
👉 GitHub - Azure Bicep Portfolio Project
And if you’ve got thoughts, tips, or projects to share — hit me up on LinkedIn! Always down to connect and learn together. 🚀