<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: ramiayoub-priv</title>
    <description>The latest articles on Forem by ramiayoub-priv (@ramiayoubpriv).</description>
    <link>https://forem.com/ramiayoubpriv</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3880755%2F7a9d5306-559c-481a-a325-dbb4a1a9aeb8.png</url>
      <title>Forem: ramiayoub-priv</title>
      <link>https://forem.com/ramiayoubpriv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ramiayoubpriv"/>
    <language>en</language>
    <item>
      <title>Azure Networking from Zero to Enterprise — Part 2: Bicep &amp; Azure Verified Modules</title>
      <dc:creator>ramiayoub-priv</dc:creator>
      <pubDate>Wed, 15 Apr 2026 16:34:33 +0000</pubDate>
      <link>https://forem.com/ramiayoubpriv/azure-networking-from-zero-to-enterprise-part-2-bicep-azure-verified-modules-55a6</link>
      <guid>https://forem.com/ramiayoubpriv/azure-networking-from-zero-to-enterprise-part-2-bicep-azure-verified-modules-55a6</guid>
      <description>&lt;h1&gt;
  
  
  Azure Networking from Zero to Enterprise — Part 2: Bicep &amp;amp; Azure Verified Modules
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;This is Part 2 of the series. In &lt;a href="https://thecloudarchitect.hashnode.dev/azure-networking-from-zero-to-enterprise-part-1-networking-basics" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, we covered VNets, Subnets, NSGs, Route Tables, DNS, and Private Endpoints. Now we're picking up the tool we'll use to deploy all of it: Bicep.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn in this part:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What Bicep is and why it exists&lt;/li&gt;
&lt;li&gt;Bicep vs ARM templates vs Terraform — an honest comparison&lt;/li&gt;
&lt;li&gt;Azure Verified Modules (AVM) — Microsoft's official module library&lt;/li&gt;
&lt;li&gt;Writing and deploying your first VNet with subnets and NSGs in Bicep&lt;/li&gt;
&lt;li&gt;Structuring a Bicep project for a multi-part series&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is Bicep?
&lt;/h2&gt;

&lt;p&gt;Bicep is Azure's domain-specific language (DSL) for deploying Azure resources. It compiles down to ARM templates (JSON), but you never have to write or read that JSON yourself.&lt;/p&gt;

&lt;p&gt;If you've ever opened a 500-line ARM template and felt your soul leave your body — Bicep is the fix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// This is Bicep. Clean, readable, no curly-brace hell.
resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' = {
  name: 'vnet-hub'
  location: 'eastus2'
  properties: {
    addressSpace: {
      addressPrefixes: ['10.10.0.0/16']
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The equivalent ARM JSON is roughly 3x longer. Bicep gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type safety&lt;/strong&gt; — intellisense and compile-time validation in VS Code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modules&lt;/strong&gt; — split your deployment into reusable pieces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No state file&lt;/strong&gt; — unlike Terraform, Bicep is stateless. Azure IS the state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day-zero support&lt;/strong&gt; — new Azure features are available in Bicep immediately. Terraform providers can lag weeks or months.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installing Bicep
&lt;/h3&gt;

&lt;p&gt;Bicep ships with the Azure CLI. If you have &lt;code&gt;az&lt;/code&gt; installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az bicep &lt;span class="nb"&gt;install
&lt;/span&gt;az bicep upgrade
az bicep version
&lt;span class="c"&gt;# Should show v0.30+ (as of 2026)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the &lt;strong&gt;Bicep VS Code extension&lt;/strong&gt; — it gives you autocomplete, error highlighting, and resource snippet generation. It's essential.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bicep vs Terraform — An Honest Take
&lt;/h2&gt;

&lt;p&gt;This is a hot topic, so let me be direct:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Bicep&lt;/th&gt;
&lt;th&gt;Terraform&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-cloud&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Azure only&lt;/td&gt;
&lt;td&gt;Multi-cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None (Azure is the state)&lt;/td&gt;
&lt;td&gt;State file required (local, S3, blob, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;New Azure feature support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Immediate (same day as ARM)&lt;/td&gt;
&lt;td&gt;Delayed (depends on provider release)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lower (if you know Azure)&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Module ecosystem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AVM (growing fast)&lt;/td&gt;
&lt;td&gt;Terraform Registry (massive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Drift detection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What-if deployments&lt;/td&gt;
&lt;td&gt;&lt;code&gt;terraform plan&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Declarative DSL&lt;/td&gt;
&lt;td&gt;HCL (declarative DSL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tooling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;VS Code extension, Azure CLI&lt;/td&gt;
&lt;td&gt;VS Code extension, Terraform CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  When to use Bicep:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You're &lt;strong&gt;Azure-only&lt;/strong&gt; (most enterprises with Microsoft EA agreements)&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;no state file headaches&lt;/strong&gt; (no backend config, no state locking, no state corruption)&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;day-zero support&lt;/strong&gt; for new Azure features&lt;/li&gt;
&lt;li&gt;Your team already knows Azure and ARM concepts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to use Terraform:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You deploy to &lt;strong&gt;multiple clouds&lt;/strong&gt; (AWS + Azure + GCP)&lt;/li&gt;
&lt;li&gt;You need to manage &lt;strong&gt;non-Azure resources&lt;/strong&gt; (GitHub repos, Datadog monitors, PagerDuty, etc.)&lt;/li&gt;
&lt;li&gt;Your team already knows HCL&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  My take:
&lt;/h3&gt;

&lt;p&gt;For Azure-native networking (which is what this series is about), &lt;strong&gt;Bicep wins&lt;/strong&gt;. No state file means fewer things to break, and you get new networking features the same day Microsoft releases them. I've seen Terraform's AzureRM provider lag behind on features like Private Endpoint subnet-level policies, DNS Private Resolver, and Virtual Network Manager — features we'll use in this series.&lt;/p&gt;




&lt;h2&gt;
  
  
  Azure Verified Modules (AVM)
&lt;/h2&gt;

&lt;p&gt;Here's where it gets interesting. Microsoft maintains an &lt;strong&gt;official library of production-ready Bicep modules&lt;/strong&gt; called &lt;a href="https://aka.ms/avm" rel="noopener noreferrer"&gt;Azure Verified Modules&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why AVM matters:
&lt;/h3&gt;

&lt;p&gt;Without AVM, deploying a VNet with subnets, NSGs, and route tables means writing 200+ lines of Bicep. With AVM, it's ~30 lines.&lt;/p&gt;

&lt;p&gt;AVM modules are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Microsoft-maintained&lt;/strong&gt; and regularly updated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well-tested&lt;/strong&gt; with CI/CD pipelines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best-practice by default&lt;/strong&gt; — they include diagnostic settings, locks, RBAC, tags&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Published to the Bicep public registry&lt;/strong&gt; — you reference them with a one-liner&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using an AVM module:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Reference the AVM Virtual Network module from the public registry
module vnet 'br/public:avm/res/network/virtual-network:0.5.2' = {
  name: 'deploy-vnet-hub'
  params: {
    name: 'vnet-hub'
    location: 'eastus2'
    addressPrefixes: ['10.10.0.0/16']
    subnets: [
      {
        name: 'AzureFirewallSubnet'
        addressPrefix: '10.10.1.0/26'
      }
      {
        name: 'AzureBastionSubnet'
        addressPrefix: '10.10.2.0/26'
      }
      {
        name: 'snet-shared-services'
        addressPrefix: '10.10.4.0/24'
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The module handles the rest — creating the VNet, subnets, applying default diagnostic settings, supporting optional parameters for peering, DNS servers, encryption, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding AVM modules:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AVM website:&lt;/strong&gt; &lt;a href="https://aka.ms/avm" rel="noopener noreferrer"&gt;https://aka.ms/avm&lt;/a&gt; — browse all available modules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bicep Registry:&lt;/strong&gt; &lt;code&gt;br/public:avm/res/&amp;lt;provider&amp;gt;/&amp;lt;resource-type&amp;gt;:&amp;lt;version&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Azure/bicep-registry-modules" rel="noopener noreferrer"&gt;https://github.com/Azure/bicep-registry-modules&lt;/a&gt; — source code, examples, and docs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Key networking modules we'll use in this series:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;Registry Path&lt;/th&gt;
&lt;th&gt;Used In&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Virtual Network&lt;/td&gt;
&lt;td&gt;&lt;code&gt;br/public:avm/res/network/virtual-network&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NSG&lt;/td&gt;
&lt;td&gt;&lt;code&gt;br/public:avm/res/network/network-security-group&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route Table&lt;/td&gt;
&lt;td&gt;&lt;code&gt;br/public:avm/res/network/route-table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 3, 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VNet Peering&lt;/td&gt;
&lt;td&gt;(part of VNet module)&lt;/td&gt;
&lt;td&gt;Part 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Firewall&lt;/td&gt;
&lt;td&gt;&lt;code&gt;br/public:avm/res/network/azure-firewall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ExpressRoute Gateway&lt;/td&gt;
&lt;td&gt;&lt;code&gt;br/public:avm/res/network/virtual-network-gateway&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bastion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;br/public:avm/res/network/bastion-host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Hands-On: Your First Bicep Deployment
&lt;/h2&gt;

&lt;p&gt;Let's deploy the hub VNet from Part 1's design. We'll do it two ways: raw Bicep and then with AVM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option A: Raw Bicep
&lt;/h3&gt;

&lt;p&gt;Create a file called &lt;code&gt;main.bicep&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ============================================
// Part 2: Hub VNet with Subnets and NSGs
// ============================================

targetScope = 'resourceGroup'

@description('Azure region for all resources')
param location string = resourceGroup().location

@description('Environment name used for naming')
param environment string = 'dev'

// --- NSG for shared services subnet ---
resource nsgSharedServices 'Microsoft.Network/networkSecurityGroups@2024-01-01' = {
  name: 'nsg-${environment}-shared-services'
  location: location
  properties: {
    securityRules: [
      {
        name: 'Allow-DNS-Inbound'
        properties: {
          priority: 100
          direction: 'Inbound'
          access: 'Allow'
          protocol: '*'
          sourcePortRange: '*'
          destinationPortRange: '53'
          sourceAddressPrefix: '10.0.0.0/8'
          destinationAddressPrefix: '*'
        }
      }
    ]
  }
}

// --- NSG for management subnet ---
resource nsgManagement 'Microsoft.Network/networkSecurityGroups@2024-01-01' = {
  name: 'nsg-${environment}-management'
  location: location
  properties: {
    securityRules: [
      {
        name: 'Deny-All-Inbound'
        properties: {
          priority: 4096
          direction: 'Inbound'
          access: 'Deny'
          protocol: '*'
          sourcePortRange: '*'
          destinationPortRange: '*'
          sourceAddressPrefix: '*'
          destinationAddressPrefix: '*'
        }
      }
    ]
  }
}

// --- Hub VNet ---
resource vnetHub 'Microsoft.Network/virtualNetworks@2024-01-01' = {
  name: 'vnet-${environment}-hub'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: ['10.10.0.0/16']
    }
    subnets: [
      {
        name: 'GatewaySubnet'
        properties: {
          addressPrefix: '10.10.0.0/26'
        }
      }
      {
        name: 'AzureFirewallSubnet'
        properties: {
          addressPrefix: '10.10.1.0/26'
        }
      }
      {
        name: 'AzureBastionSubnet'
        properties: {
          addressPrefix: '10.10.2.0/26'
        }
      }
      {
        name: 'snet-management'
        properties: {
          addressPrefix: '10.10.3.0/24'
          networkSecurityGroup: {
            id: nsgManagement.id
          }
        }
      }
      {
        name: 'snet-shared-services'
        properties: {
          addressPrefix: '10.10.4.0/24'
          networkSecurityGroup: {
            id: nsgSharedServices.id
          }
        }
      }
    ]
  }
}

// --- Outputs ---
output vnetId string = vnetHub.id
output vnetName string = vnetHub.name
output subnetIds array = [for subnet in vnetHub.properties.subnets: subnet.id]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy it:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a resource group&lt;/span&gt;
az group create &lt;span class="nt"&gt;--name&lt;/span&gt; rg-networking-dev &lt;span class="nt"&gt;--location&lt;/span&gt; eastus2

&lt;span class="c"&gt;# Deploy&lt;/span&gt;
az deployment group create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; rg-networking-dev &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-file&lt;/span&gt; main.bicep &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameters&lt;/span&gt; &lt;span class="nv"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev

&lt;span class="c"&gt;# What-if (preview changes without deploying)&lt;/span&gt;
az deployment group what-if &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; rg-networking-dev &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-file&lt;/span&gt; main.bicep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option B: The AVM Way (Recommended)
&lt;/h3&gt;

&lt;p&gt;Same result, less code, more features out of the box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ============================================
// Part 2: Hub VNet using AVM Module
// ============================================

targetScope = 'resourceGroup'

param location string = resourceGroup().location
param environment string = 'dev'

// --- NSGs ---
module nsgSharedServices 'br/public:avm/res/network/network-security-group:0.5.0' = {
  name: 'deploy-nsg-shared-services'
  params: {
    name: 'nsg-${environment}-shared-services'
    location: location
    securityRules: [
      {
        name: 'Allow-DNS-Inbound'
        properties: {
          priority: 100
          direction: 'Inbound'
          access: 'Allow'
          protocol: '*'
          sourcePortRange: '*'
          destinationPortRange: '53'
          sourceAddressPrefix: '10.0.0.0/8'
          destinationAddressPrefix: '*'
        }
      }
    ]
  }
}

module nsgManagement 'br/public:avm/res/network/network-security-group:0.5.0' = {
  name: 'deploy-nsg-management'
  params: {
    name: 'nsg-${environment}-management'
    location: location
  }
}

// --- Hub VNet ---
module vnetHub 'br/public:avm/res/network/virtual-network:0.5.2' = {
  name: 'deploy-vnet-hub'
  params: {
    name: 'vnet-${environment}-hub'
    location: location
    addressPrefixes: ['10.10.0.0/16']
    subnets: [
      {
        name: 'GatewaySubnet'
        addressPrefix: '10.10.0.0/26'
      }
      {
        name: 'AzureFirewallSubnet'
        addressPrefix: '10.10.1.0/26'
      }
      {
        name: 'AzureBastionSubnet'
        addressPrefix: '10.10.2.0/26'
      }
      {
        name: 'snet-management'
        addressPrefix: '10.10.3.0/24'
        networkSecurityGroupResourceId: nsgManagement.outputs.resourceId
      }
      {
        name: 'snet-shared-services'
        addressPrefix: '10.10.4.0/24'
        networkSecurityGroupResourceId: nsgSharedServices.outputs.resourceId
      }
    ]
  }
}

output vnetId string = vnetHub.outputs.resourceId
output vnetName string = vnetHub.outputs.name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What's different with AVM?
&lt;/h3&gt;

&lt;p&gt;Notice we didn't configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Diagnostic settings&lt;/li&gt;
&lt;li&gt;Resource locks&lt;/li&gt;
&lt;li&gt;Tags&lt;/li&gt;
&lt;li&gt;RBAC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AVM module &lt;strong&gt;supports all of these as optional parameters&lt;/strong&gt;. When you need them, you add one line. When you don't, the module does the right thing by default.&lt;/p&gt;

&lt;p&gt;Compare: the raw Bicep version is 100+ lines and only covers the basics. The AVM version is ~60 lines and gives you access to every feature the VNet resource supports — diagnostic settings, DNS servers, peering, encryption, flow timeout — all as optional params.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Structure for This Series
&lt;/h2&gt;

&lt;p&gt;Here's how I organize the companion repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;azure-networking-series/
├── part2-bicep/
│   ├── main.bicep           # Hub VNet deployment
│   ├── main.bicepparam      # Parameter file
│   └── README.md
├── part3-hub-spoke/
│   ├── main.bicep           # Hub + Spoke VNets + Peering
│   └── ...
├── part4-firewall/
│   ├── main.bicep           # NVA/Firewall + UDRs
│   └── ...
├── full-architecture/
│   ├── main.bicep           # Complete deployment
│   ├── modules/
│   │   ├── hub.bicep
│   │   ├── spoke.bicep
│   │   └── connectivity.bicep
│   └── README.md
└── README.md                # Series overview + architecture diagram
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each part's folder is self-contained — you can &lt;code&gt;az deployment group create&lt;/code&gt; from any part folder and get a working deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bicep Tips That'll Save You Time
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Always use &lt;code&gt;what-if&lt;/code&gt; before deploying
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az deployment group what-if &lt;span class="nt"&gt;--resource-group&lt;/span&gt; rg-networking-dev &lt;span class="nt"&gt;--template-file&lt;/span&gt; main.bicep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows you exactly what will be created, modified, or deleted — without touching anything. Use it every single time.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use &lt;code&gt;.bicepparam&lt;/code&gt; files instead of inline parameters
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// main.bicepparam
using './main.bicep'

param location = 'eastus2'
param environment = 'prod'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az deployment group create &lt;span class="nt"&gt;--resource-group&lt;/span&gt; rg-networking-prod &lt;span class="nt"&gt;--parameters&lt;/span&gt; main.bicepparam
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cleaner than &lt;code&gt;--parameters environment=prod location=eastus2&lt;/code&gt; and you can commit them to Git per environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use the VS Code extension's resource snippet
&lt;/h3&gt;

&lt;p&gt;Type &lt;code&gt;res-&lt;/code&gt; in VS Code and you'll get autocomplete for every Azure resource type. It generates the full resource skeleton with the latest API version.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Leverage &lt;code&gt;dependsOn&lt;/code&gt; only when Bicep can't infer it
&lt;/h3&gt;

&lt;p&gt;Bicep automatically detects dependencies when you reference one resource in another (like we did with &lt;code&gt;nsgManagement.id&lt;/code&gt; in the subnet). You only need explicit &lt;code&gt;dependsOn&lt;/code&gt; for implicit dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Use &lt;code&gt;@description&lt;/code&gt; decorators
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@description('The environment name (dev, staging, prod)')
@allowed(['dev', 'staging', 'prod'])
param environment string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These show up in the Azure Portal when someone deploys your template manually. Good practice, zero effort.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Key Takeaway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bicep&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Azure's native IaC language. Clean syntax, no state file, day-zero feature support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bicep vs Terraform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use Bicep for Azure-only. Use Terraform for multi-cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AVM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microsoft's official module library. Production-ready, well-tested, saves you 50-70% of code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;what-if&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always preview before deploying. Always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Project structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One folder per part, self-contained, with parameter files per environment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 3 and Part 4 are live!&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://routetozero.dev/blog/post.html?p=part3-hub-spoke" rel="noopener noreferrer"&gt;Part 3: Hub/Spoke Architecture Deep Dive&lt;/a&gt;&lt;/strong&gt; — VNet peering, transit routing, shared services, and why hub/spoke is the default enterprise topology — deployed with Bicep and AVM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://routetozero.dev/blog/post.html?p=part4-nva-firewall" rel="noopener noreferrer"&gt;Part 4: Firewalling with Azure Firewall &amp;amp; NVAs&lt;/a&gt;&lt;/strong&gt; — Azure Firewall vs Palo Alto NVAs, routing through the firewall, SNAT/DNAT, security policies, and cost optimization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read the full series at &lt;a href="https://routetozero.dev/blog/" rel="noopener noreferrer"&gt;routetozero.dev/blog&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📘 &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview" rel="noopener noreferrer"&gt;Bicep documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📘 &lt;a href="https://aka.ms/avm" rel="noopener noreferrer"&gt;AVM Module Index&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📘 &lt;a href="https://learn.microsoft.com/en-us/credentials/certifications/azure-network-engineer-associate/" rel="noopener noreferrer"&gt;AZ-700 Study Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🛠️ &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-bicep" rel="noopener noreferrer"&gt;Bicep VS Code Extension&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;I'm a cloud network engineer specializing in Azure enterprise architectures — hub/spoke, ExpressRoute, NVAs, and infrastructure-as-code with Bicep. If you're building something similar in your org and want a second pair of eyes, feel free to reach out.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Follow this blog to get notified when Part 3 drops — we're building the hub/spoke topology. Drop your questions in the comments, I read every one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>bicep</category>
      <category>iac</category>
      <category>devops</category>
    </item>
    <item>
      <title>Azure Networking from Zero to Enterprise — Part 1: Networking Basics</title>
      <dc:creator>ramiayoub-priv</dc:creator>
      <pubDate>Wed, 15 Apr 2026 15:31:08 +0000</pubDate>
      <link>https://forem.com/ramiayoubpriv/azure-networking-from-zero-to-enterprise-part-1-networking-basics-5c66</link>
      <guid>https://forem.com/ramiayoubpriv/azure-networking-from-zero-to-enterprise-part-1-networking-basics-5c66</guid>
      <description>&lt;h1&gt;
  
  
  Azure Networking from Zero to Enterprise — Part 1: Networking Basics
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;This is Part 1 of a multi-part series where we'll go from the absolute basics of Azure networking all the way to deploying a production-grade hub/spoke architecture with a firewall NVA, on-premises connectivity via ExpressRoute, and full DNS integration — all deployed with Bicep.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn in this part:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What a Virtual Network (VNet) is and how it differs from on-prem networking&lt;/li&gt;
&lt;li&gt;Subnets, address spaces, and CIDR planning&lt;/li&gt;
&lt;li&gt;Network Security Groups (NSGs) — the first line of defense&lt;/li&gt;
&lt;li&gt;Route Tables and User-Defined Routes (UDRs)&lt;/li&gt;
&lt;li&gt;Azure DNS basics — what resolves where and why it matters&lt;/li&gt;
&lt;li&gt;Service Endpoints vs Private Endpoints — when to use which&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Azure Networking Matters
&lt;/h2&gt;

&lt;p&gt;If you're a cloud engineer, solutions architect, or DevOps professional working with Azure, networking is the foundation everything else sits on. Get it wrong, and you'll spend months untangling routing issues, firewall misconfigurations, and DNS headaches. Get it right from the start, and everything else — compute, databases, security — just clicks into place.&lt;/p&gt;

&lt;p&gt;The problem? Azure networking has a lot of moving pieces, and Microsoft's documentation, while comprehensive, is scattered across hundreds of pages. This series aims to give you the complete picture in one place, with real-world opinions and working code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Virtual Networks (VNets)
&lt;/h2&gt;

&lt;p&gt;A Virtual Network is Azure's fundamental networking construct. Think of it as your private, isolated network in the cloud — similar to a VLAN or a dedicated IP segment in your on-premises data center, but software-defined.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key properties of a VNet:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Region-scoped:&lt;/strong&gt; A VNet lives in a single Azure region. You can't stretch a VNet across regions (you'd peer two VNets for that).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Address space:&lt;/strong&gt; You assign one or more CIDR blocks (e.g., &lt;code&gt;10.0.0.0/16&lt;/code&gt;). This is the total IP range available to the VNet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation:&lt;/strong&gt; VNets are completely isolated from each other by default. Two VNets with the same address space won't conflict — they simply can't talk to each other unless you explicitly connect them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free:&lt;/strong&gt; VNets themselves are free. You pay for what goes through them (gateways, peering traffic, etc.).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating a VNet — what you need to decide:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Address space size.&lt;/strong&gt; Use RFC 1918 ranges: &lt;code&gt;10.0.0.0/8&lt;/code&gt;, &lt;code&gt;172.16.0.0/12&lt;/code&gt;, or &lt;code&gt;192.168.0.0/16&lt;/code&gt;. For enterprise, I recommend using a &lt;code&gt;/16&lt;/code&gt; per VNet (65,536 addresses) which gives you room to grow. Don't be stingy with IP space in the cloud — running out of addresses in a VNet is a pain to fix later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid overlaps.&lt;/strong&gt; If you plan to connect to on-premises networks (via VPN or ExpressRoute), your Azure address space must NOT overlap with your on-prem ranges. This is the number one mistake I see in enterprise deployments. Map out all your address spaces across on-prem and cloud before you create a single VNet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Region placement.&lt;/strong&gt; Deploy the VNet in the same region as your workloads. Cross-region traffic costs money and adds latency.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Example:
On-prem range:    10.1.0.0/16
Azure Hub VNet:   10.10.0.0/16
Azure Spoke 1:    10.20.0.0/16
Azure Spoke 2:    10.30.0.0/16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Subnets
&lt;/h2&gt;

&lt;p&gt;A subnet is a subdivision of your VNet's address space. Every resource you deploy (VMs, load balancers, private endpoints) goes into a subnet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subnet design principles:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plan ahead.&lt;/strong&gt; You can resize subnets later, but only if they're empty. Moving resources between subnets is disruptive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate by function, not by team.&lt;/strong&gt; Create subnets like &lt;code&gt;snet-workload&lt;/code&gt;, &lt;code&gt;snet-data&lt;/code&gt;, &lt;code&gt;snet-privateendpoints&lt;/code&gt;, &lt;code&gt;snet-appgw&lt;/code&gt; — not &lt;code&gt;snet-teamalpha&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure reserves 5 IPs per subnet.&lt;/strong&gt; The first 4 and the last IP address in every subnet are reserved. A &lt;code&gt;/28&lt;/code&gt; gives you 16 addresses minus 5 = 11 usable. Keep this in mind for small subnets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some services need dedicated subnets.&lt;/strong&gt; Azure Application Gateway, Azure Firewall, VPN Gateway, Azure Bastion, and several others require their own dedicated subnet, often with specific naming (like &lt;code&gt;AzureBastionSubnet&lt;/code&gt; or &lt;code&gt;GatewaySubnet&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common subnet layout for a hub VNet:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.10.0.0/16 (Hub VNet)
├── GatewaySubnet:          10.10.0.0/26    (VPN/ExpressRoute Gateway)
├── AzureFirewallSubnet:    10.10.1.0/26    (Azure Firewall or NVA)
├── AzureBastionSubnet:     10.10.2.0/26    (Azure Bastion)
├── snet-management:        10.10.3.0/24    (Jump boxes, monitoring)
├── snet-shared-services:   10.10.4.0/24    (DNS forwarders, AD DCs)
└── snet-privateendpoints:  10.10.5.0/24    (Private Endpoints for shared services)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common subnet layout for a spoke VNet:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.20.0.0/16 (Spoke VNet - Workload A)
├── snet-app:               10.20.1.0/24    (Application tier)
├── snet-data:              10.20.2.0/24    (Database tier)
├── snet-privateendpoints:  10.20.3.0/24    (Private Endpoints)
└── snet-integration:       10.20.4.0/24    (App Service VNet integration)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Network Security Groups (NSGs)
&lt;/h2&gt;

&lt;p&gt;NSGs are Azure's stateful firewall at the subnet (or NIC) level. They're your first line of defense and should be applied to &lt;strong&gt;every&lt;/strong&gt; subnet (except for those that don't support them, like &lt;code&gt;GatewaySubnet&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  How NSGs work:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An NSG contains &lt;strong&gt;inbound&lt;/strong&gt; and &lt;strong&gt;outbound&lt;/strong&gt; rules.&lt;/li&gt;
&lt;li&gt;Each rule has a &lt;strong&gt;priority&lt;/strong&gt; (100–4096, lower = processed first), &lt;strong&gt;source&lt;/strong&gt;, &lt;strong&gt;destination&lt;/strong&gt;, &lt;strong&gt;port&lt;/strong&gt;, &lt;strong&gt;protocol&lt;/strong&gt;, and &lt;strong&gt;action&lt;/strong&gt; (Allow or Deny).&lt;/li&gt;
&lt;li&gt;Azure has &lt;strong&gt;default rules&lt;/strong&gt; you can't delete (allow VNet-to-VNet, allow outbound internet, deny all inbound from internet, etc.). You can override them with higher-priority rules.&lt;/li&gt;
&lt;li&gt;NSGs are &lt;strong&gt;stateful&lt;/strong&gt; — if you allow inbound traffic on port 443, the response traffic is automatically allowed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  NSG best practices:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Apply NSGs at the subnet level&lt;/strong&gt;, not individual NICs (unless you have a very specific reason). Subnet-level NSGs are easier to manage and audit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Application Security Groups (ASGs)&lt;/strong&gt; to group VMs by role instead of using IP addresses in rules. E.g., create an ASG called &lt;code&gt;asg-webservers&lt;/code&gt; and reference it in rules — when you add a new web server, it inherits the rules automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't rely solely on NSGs&lt;/strong&gt; for east-west traffic control in enterprise. They're great for basic segmentation, but for advanced inspection (IDS/IPS, URL filtering, threat intelligence), you need a firewall NVA — which we'll cover in Part 4.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable NSG flow logs&lt;/strong&gt; for troubleshooting and compliance. Send them to a Log Analytics workspace.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example NSG rule:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow-HTTPS-Inbound&lt;/span&gt;
&lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Internet&lt;/span&gt;
&lt;span class="na"&gt;Destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;asg-webservers&lt;/span&gt;
&lt;span class="na"&gt;Port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
&lt;span class="na"&gt;Protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Route Tables and User-Defined Routes (UDRs)
&lt;/h2&gt;

&lt;p&gt;By default, Azure routes traffic between subnets within a VNet automatically (system routes). It also routes to the internet via a default &lt;code&gt;0.0.0.0/0&lt;/code&gt; route. But in enterprise architectures, you almost always need to override these defaults.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why UDRs matter:
&lt;/h3&gt;

&lt;p&gt;In a hub/spoke model (which we'll build in Part 3), you want &lt;strong&gt;all traffic to flow through a central firewall&lt;/strong&gt;. Without UDRs, traffic between spoke VNets would go directly via peering — bypassing your firewall entirely. UDRs let you force traffic through a specific next hop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key concepts:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Route Table:&lt;/strong&gt; A collection of routes that you associate with one or more subnets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-Defined Route (UDR):&lt;/strong&gt; A custom route you create to override Azure's default routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next Hop Types:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;VirtualAppliance&lt;/code&gt; — send traffic to a specific IP (your firewall NVA)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VirtualNetworkGateway&lt;/code&gt; — send to VPN/ExpressRoute gateway&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VnetLocal&lt;/code&gt; — keep within the VNet&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Internet&lt;/code&gt; — route to internet&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;None&lt;/code&gt; — drop the traffic (black hole)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example: Force all internet-bound traffic through a firewall
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route Table: rt-spoke-workload
├── Route: to-internet
│   Prefix: 0.0.0.0/0
│   Next Hop: VirtualAppliance → 10.10.1.4 (firewall NVA IP)
├── Route: to-onprem
│   Prefix: 10.1.0.0/16
│   Next Hop: VirtualAppliance → 10.10.1.4
└── Route: to-hub-shared
    Prefix: 10.10.0.0/16
    Next Hop: VirtualAppliance → 10.10.1.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; When you associate a route table with a subnet, it only affects traffic &lt;em&gt;leaving&lt;/em&gt; that subnet. The firewall's own subnet should NOT have a UDR pointing to itself (routing loop). This is a common mistake.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Azure DNS Basics
&lt;/h2&gt;

&lt;p&gt;DNS in Azure is one of those things that seems simple until you start dealing with hybrid environments. Let me break down the layers:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Azure-provided DNS (default)
&lt;/h3&gt;

&lt;p&gt;Every VNet gets Azure's built-in DNS (168.63.129.16) for free. It resolves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public DNS names (google.com, etc.)&lt;/li&gt;
&lt;li&gt;Azure internal names (&lt;code&gt;vmname.internal.cloudapp.net&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Endpoint DNS&lt;/strong&gt; (if integrated correctly)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For simple deployments, this is fine. For enterprise with on-prem connectivity, you'll need more.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Azure DNS Public Zones
&lt;/h3&gt;

&lt;p&gt;This is Azure's managed DNS for &lt;strong&gt;public&lt;/strong&gt; domains you own. Instead of using GoDaddy/Cloudflare for DNS hosting, you can point your domain's nameservers to Azure DNS. Not directly relevant to networking architecture, but useful to know.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Azure DNS Private Zones
&lt;/h3&gt;

&lt;p&gt;This is where it gets important for enterprise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Private DNS Zone (e.g., &lt;code&gt;privatelink.database.windows.net&lt;/code&gt;) allows you to create DNS records that are only resolvable within your VNets.&lt;/li&gt;
&lt;li&gt;When you create a Private Endpoint for an Azure service (like SQL Database), Azure can auto-create a DNS record in the linked Private Zone so &lt;code&gt;mydb.database.windows.net&lt;/code&gt; resolves to the private IP.&lt;/li&gt;
&lt;li&gt;You &lt;strong&gt;link&lt;/strong&gt; Private DNS Zones to VNets. Linked VNets can resolve records in the zone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. The Hybrid DNS Problem
&lt;/h3&gt;

&lt;p&gt;When you connect on-premises to Azure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On-prem servers need to resolve Azure private DNS names (e.g., &lt;code&gt;mydb.privatelink.database.windows.net&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Azure VMs might need to resolve on-prem DNS names (e.g., &lt;code&gt;dc01.corp.local&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution: Conditional Forwarders&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hocon"&gt;&lt;code&gt;&lt;span class="l"&gt;On-prem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;DNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"For *.privatelink.database.windows.net → forward to 10.10.5.10"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;DNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;forwarder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;VM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Azure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="l"&gt;Azure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;DNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Forwarder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;10.10.5.10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"For *.corp.local → forward to 10.1.0.5"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;on-prem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;DC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"Everything else → forward to 168.63.129.16"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;Azure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;DNS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll set this up properly in Part 5 with actual configuration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Azure now offers &lt;strong&gt;DNS Private Resolver&lt;/strong&gt; as a managed service that replaces the need for DNS forwarder VMs. We'll cover both approaches.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Service Endpoints vs Private Endpoints
&lt;/h2&gt;

&lt;p&gt;Both are designed to let your VNet resources access Azure PaaS services (Storage, SQL, Key Vault, etc.) privately. But they work very differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service Endpoints
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Enables a &lt;strong&gt;direct route&lt;/strong&gt; from your subnet to the Azure service over Microsoft's backbone network.&lt;/li&gt;
&lt;li&gt;The PaaS service still has a &lt;strong&gt;public IP&lt;/strong&gt;, but traffic from your subnet takes an optimized private path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free&lt;/strong&gt; to use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitations:&lt;/strong&gt; Only works from the VNet with the service endpoint enabled. Doesn't work from on-premises or peered VNets (without extra config). The PaaS service's public endpoint still exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Private Endpoints
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Creates a &lt;strong&gt;private IP address&lt;/strong&gt; in your subnet that maps to a specific PaaS resource.&lt;/li&gt;
&lt;li&gt;Traffic goes entirely over your private network. The PaaS service effectively gets a NIC in your VNet.&lt;/li&gt;
&lt;li&gt;Works from &lt;strong&gt;anywhere&lt;/strong&gt; that can reach the private IP — other VNets, on-prem via VPN/ExpressRoute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Costs money:&lt;/strong&gt; ~$7.30/month per endpoint + $0.01/GB processed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requires DNS configuration&lt;/strong&gt; (Private DNS Zones) to resolve the service name to the private IP.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to use which:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple workload, single VNet, no on-prem&lt;/td&gt;
&lt;td&gt;Service Endpoint (free, easy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise, hub/spoke, on-prem connectivity&lt;/td&gt;
&lt;td&gt;Private Endpoint (more secure, works everywhere)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance requires no public IP on PaaS&lt;/td&gt;
&lt;td&gt;Private Endpoint + disable public access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this series, we'll primarily use &lt;strong&gt;Private Endpoints&lt;/strong&gt; because we're building an enterprise architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Here's what we covered:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;th&gt;Key Takeaway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VNet&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolated private network in Azure&lt;/td&gt;
&lt;td&gt;Use /16 per VNet, avoid address space overlaps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subnet&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Segment within a VNet&lt;/td&gt;
&lt;td&gt;Plan by function, remember Azure reserves 5 IPs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NSG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stateful firewall at subnet/NIC level&lt;/td&gt;
&lt;td&gt;Apply to every subnet, use ASGs, enable flow logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Route Table / UDR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Override default routing&lt;/td&gt;
&lt;td&gt;Essential for forcing traffic through a firewall&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Azure DNS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Name resolution&lt;/td&gt;
&lt;td&gt;Private Zones + conditional forwarding for hybrid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service Endpoints&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optimized path to PaaS&lt;/td&gt;
&lt;td&gt;Free, simple, limited to local VNet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Private Endpoints&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Private IP for PaaS services&lt;/td&gt;
&lt;td&gt;Enterprise-grade, works with on-prem, needs DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt;, we'll cover &lt;strong&gt;Bicep and Azure Verified Modules (AVM)&lt;/strong&gt; — the infrastructure-as-code language we'll use to deploy everything in this series. You'll learn why Bicep over Terraform (for Azure-native work), how AVM modules save you from reinventing the wheel, and we'll deploy our first VNet with subnets and NSGs in code.&lt;/p&gt;

&lt;p&gt;If you want to follow along with the code, the companion repo will be linked here once Part 2 drops.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;If you're studying for the &lt;strong&gt;AZ-700 (Azure Network Engineer Associate)&lt;/strong&gt; certification, this series covers most of the exam objectives. Here's the official study path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📘 &lt;a href="https://learn.microsoft.com/en-us/credentials/certifications/azure-network-engineer-associate/" rel="noopener noreferrer"&gt;Microsoft Learn: AZ-700 Study Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🛠️ &lt;a href="https://app.diagrams.net/" rel="noopener noreferrer"&gt;diagrams.net&lt;/a&gt; — Free tool for drawing Azure network diagrams (I use it for all my architecture work)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;I'm a cloud network engineer specializing in Azure enterprise architectures — hub/spoke, ExpressRoute, NVAs, and infrastructure-as-code with Bicep. If you're building something similar in your org and want a second pair of eyes, feel free to reach out.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Follow this blog to get notified when Part 2 drops — we're building all of this in Bicep. Drop your questions in the comments, I read every one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>networking</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
