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 and prod 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 and backend 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. 🚀