Azure Infrastructure: From Basics to Production Patterns

Introduction

Azure’s vastness can be overwhelming—hundreds of services, multiple ways to accomplish the same thing, and pricing models that require a spreadsheet to understand. After managing Azure infrastructure for several years, I’ve developed patterns that balance capability with cost.

This guide covers practical Azure infrastructure: networking fundamentals, compute choices, security patterns, and cost optimization strategies.

Azure Networking Fundamentals

Virtual Network Architecture

Start with a hub-and-spoke topology:

                    ┌─────────────────────┐
                    │     Hub VNet        │
                    │   (10.0.0.0/16)     │
                    │                     │
                    │  ┌───────────────┐  │
                    │  │ Gateway Subnet│  │───── VPN/ExpressRoute
                    │  │ (10.0.1.0/24) │  │
                    │  └───────────────┘  │
                    │  ┌───────────────┐  │
                    │  │ Firewall      │  │
                    │  │ (10.0.2.0/24) │  │
                    │  └───────────────┘  │
                    └──────────┬──────────┘
                               │ Peering
         ┌─────────────────────┼─────────────────────┐
         │                     │                     │
┌────────▼────────┐   ┌────────▼────────┐   ┌────────▼────────┐
│  Spoke: Prod    │   │  Spoke: Dev     │   │  Spoke: Mgmt    │
│ (10.1.0.0/16)   │   │ (10.2.0.0/16)   │   │ (10.3.0.0/16)   │
│                 │   │                 │   │                 │
│ Web: 10.1.1.0/24│   │ Web: 10.2.1.0/24│   │ Jump: 10.3.1.0/24│
│ App: 10.1.2.0/24│   │ App: 10.2.2.0/24│   │ Mon:  10.3.2.0/24│
│ DB:  10.1.3.0/24│   │ DB:  10.2.3.0/24│   │                 │
└─────────────────┘   └─────────────────┘   └─────────────────┘

Infrastructure as Code with Bicep

// main.bicep
targetScope = 'subscription'

param location string = 'eastus'
param environment string = 'prod'

// Resource Group
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
  name: 'rg-infra-${environment}'
  location: location
}

// Hub Virtual Network
module hubVnet 'modules/vnet.bicep' = {
  scope: rg
  name: 'hubVnet'
  params: {
    name: 'vnet-hub-${environment}'
    location: location
    addressPrefix: '10.0.0.0/16'
    subnets: [
      {
        name: 'GatewaySubnet'
        addressPrefix: '10.0.1.0/24'
      }
      {
        name: 'AzureFirewallSubnet'
        addressPrefix: '10.0.2.0/24'
      }
      {
        name: 'snet-management'
        addressPrefix: '10.0.3.0/24'
      }
    ]
  }
}

// Production Spoke
module prodVnet 'modules/vnet.bicep' = {
  scope: rg
  name: 'prodVnet'
  params: {
    name: 'vnet-prod-${environment}'
    location: location
    addressPrefix: '10.1.0.0/16'
    subnets: [
      {
        name: 'snet-web'
        addressPrefix: '10.1.1.0/24'
        nsgId: webNsg.outputs.id
      }
      {
        name: 'snet-app'
        addressPrefix: '10.1.2.0/24'
        nsgId: appNsg.outputs.id
      }
      {
        name: 'snet-db'
        addressPrefix: '10.1.3.0/24'
        nsgId: dbNsg.outputs.id
      }
    ]
  }
}

// VNet Peering
module peering 'modules/peering.bicep' = {
  scope: rg
  name: 'hubToProdPeering'
  params: {
    hubVnetId: hubVnet.outputs.id
    spokeVnetId: prodVnet.outputs.id
  }
}
// modules/vnet.bicep
param name string
param location string
param addressPrefix string
param subnets array

resource vnet 'Microsoft.Network/virtualNetworks@2021-08-01' = {
  name: name
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [addressPrefix]
    }
    subnets: [for subnet in subnets: {
      name: subnet.name
      properties: {
        addressPrefix: subnet.addressPrefix
        networkSecurityGroup: contains(subnet, 'nsgId') ? {
          id: subnet.nsgId
        } : null
        privateEndpointNetworkPolicies: 'Disabled'
      }
    }]
  }
}

output id string = vnet.id
output name string = vnet.name

Network Security Groups

// modules/nsg.bicep
param name string
param location string
param rules array

resource nsg 'Microsoft.Network/networkSecurityGroups@2021-08-01' = {
  name: name
  location: location
  properties: {
    securityRules: rules
  }
}

output id string = nsg.id
// Web tier NSG example
module webNsg 'modules/nsg.bicep' = {
  scope: rg
  name: 'webNsg'
  params: {
    name: 'nsg-web-${environment}'
    location: location
    rules: [
      {
        name: 'AllowHTTPS'
        properties: {
          priority: 100
          direction: 'Inbound'
          access: 'Allow'
          protocol: 'Tcp'
          sourceAddressPrefix: 'Internet'
          sourcePortRange: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '443'
        }
      }
      {
        name: 'AllowLoadBalancer'
        properties: {
          priority: 110
          direction: 'Inbound'
          access: 'Allow'
          protocol: '*'
          sourceAddressPrefix: 'AzureLoadBalancer'
          sourcePortRange: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '*'
        }
      }
      {
        name: 'DenyAllInbound'
        properties: {
          priority: 4096
          direction: 'Inbound'
          access: 'Deny'
          protocol: '*'
          sourceAddressPrefix: '*'
          sourcePortRange: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '*'
        }
      }
    ]
  }
}

Compute Patterns

VM Sizing Guide

WorkloadSeriesExampleUse Case
GeneralDD4s_v5Web servers, apps
ComputeFF8s_v2Batch processing
MemoryEE8s_v5Databases, caching
StorageLL8s_v3Data warehouses
GPUNCNC6s_v3ML training

Cost-Effective VM Strategy

// Spot VMs for non-critical workloads
resource spotVm 'Microsoft.Compute/virtualMachines@2021-11-01' = {
  name: 'vm-worker-${uniqueString(resourceGroup().id)}'
  location: location
  properties: {
    hardwareProfile: {
      vmSize: 'Standard_D4s_v5'
    }
    priority: 'Spot'
    evictionPolicy: 'Deallocate'
    billingProfile: {
      maxPrice: 0.1  // Max price per hour
    }
    // ... other properties
  }
}

VM Scale Sets

resource vmss 'Microsoft.Compute/virtualMachineScaleSets@2021-11-01' = {
  name: 'vmss-web-${environment}'
  location: location
  sku: {
    name: 'Standard_D2s_v5'
    tier: 'Standard'
    capacity: 2
  }
  properties: {
    overprovision: true
    upgradePolicy: {
      mode: 'Rolling'
      rollingUpgradePolicy: {
        maxBatchInstancePercent: 25
        maxUnhealthyInstancePercent: 25
        pauseTimeBetweenBatches: 'PT5S'
      }
    }
    virtualMachineProfile: {
      osProfile: {
        computerNamePrefix: 'web'
        adminUsername: 'azureuser'
        linuxConfiguration: {
          disablePasswordAuthentication: true
          ssh: {
            publicKeys: [
              {
                path: '/home/azureuser/.ssh/authorized_keys'
                keyData: sshPublicKey
              }
            ]
          }
        }
      }
      storageProfile: {
        osDisk: {
          createOption: 'FromImage'
          managedDisk: {
            storageAccountType: 'Premium_LRS'
          }
        }
        imageReference: {
          publisher: 'Canonical'
          offer: '0001-com-ubuntu-server-jammy'
          sku: '22_04-lts-gen2'
          version: 'latest'
        }
      }
      networkProfile: {
        networkInterfaceConfigurations: [
          {
            name: 'nic-config'
            properties: {
              primary: true
              ipConfigurations: [
                {
                  name: 'ipconfig1'
                  properties: {
                    subnet: {
                      id: webSubnetId
                    }
                    loadBalancerBackendAddressPools: [
                      {
                        id: loadBalancer.properties.backendAddressPools[0].id
                      }
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}

// Autoscale settings
resource autoscale 'Microsoft.Insights/autoscalesettings@2021-05-01-preview' = {
  name: 'autoscale-vmss'
  location: location
  properties: {
    targetResourceUri: vmss.id
    profiles: [
      {
        name: 'default'
        capacity: {
          minimum: '2'
          maximum: '10'
          default: '2'
        }
        rules: [
          {
            metricTrigger: {
              metricName: 'Percentage CPU'
              metricResourceUri: vmss.id
              timeGrain: 'PT1M'
              statistic: 'Average'
              timeWindow: 'PT5M'
              timeAggregation: 'Average'
              operator: 'GreaterThan'
              threshold: 75
            }
            scaleAction: {
              direction: 'Increase'
              type: 'ChangeCount'
              value: '1'
              cooldown: 'PT5M'
            }
          }
          {
            metricTrigger: {
              metricName: 'Percentage CPU'
              metricResourceUri: vmss.id
              timeGrain: 'PT1M'
              statistic: 'Average'
              timeWindow: 'PT5M'
              timeAggregation: 'Average'
              operator: 'LessThan'
              threshold: 25
            }
            scaleAction: {
              direction: 'Decrease'
              type: 'ChangeCount'
              value: '1'
              cooldown: 'PT5M'
            }
          }
        ]
      }
    ]
  }
}

Security Patterns

Private Endpoints

Keep data plane traffic off the internet:

// Private endpoint for Azure SQL
resource sqlPrivateEndpoint 'Microsoft.Network/privateEndpoints@2021-08-01' = {
  name: 'pe-sql-${environment}'
  location: location
  properties: {
    subnet: {
      id: dbSubnetId
    }
    privateLinkServiceConnections: [
      {
        name: 'sql-connection'
        properties: {
          privateLinkServiceId: sqlServer.id
          groupIds: ['sqlServer']
        }
      }
    ]
  }
}

// DNS integration
resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
  name: 'privatelink.database.windows.net'
  location: 'global'
}

resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
  parent: privateDnsZone
  name: 'link-hub'
  location: 'global'
  properties: {
    virtualNetwork: {
      id: hubVnetId
    }
    registrationEnabled: false
  }
}

Key Vault Integration

// Key Vault with RBAC
resource keyVault 'Microsoft.KeyVault/vaults@2021-11-01-preview' = {
  name: 'kv-${environment}-${uniqueString(resourceGroup().id)}'
  location: location
  properties: {
    tenantId: subscription().tenantId
    sku: {
      family: 'A'
      name: 'standard'
    }
    enableRbacAuthorization: true
    enableSoftDelete: true
    softDeleteRetentionInDays: 90
    enablePurgeProtection: true
    networkAcls: {
      defaultAction: 'Deny'
      bypass: 'AzureServices'
      virtualNetworkRules: [
        {
          id: managementSubnetId
        }
      ]
    }
  }
}

// Managed identity for VMs
resource vmIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
  name: 'id-vm-${environment}'
  location: location
}

// Grant secret access
resource secretsUserRole 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = {
  scope: keyVault
  name: guid(keyVault.id, vmIdentity.id, 'secrets-user')
  properties: {
    roleDefinitionId: subscriptionResourceId(
      'Microsoft.Authorization/roleDefinitions',
      '4633458b-17de-408a-b874-0445c86b69e6'  // Key Vault Secrets User
    )
    principalId: vmIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

Cost Optimization

Reserved Instances Strategy

# Analyze VM usage for RI recommendations
from azure.mgmt.consumption import ConsumptionManagementClient
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
client = ConsumptionManagementClient(credential, subscription_id)

# Get reservation recommendations
recommendations = client.reservation_recommendations.list(
    scope=f'/subscriptions/{subscription_id}',
    filter="properties/lookBackPeriod eq 'Last30Days'"
)

for rec in recommendations:
    print(f"VM Size: {rec.recommended_quantity}")
    print(f"Estimated savings: ${rec.net_savings}")
    print(f"Term: {rec.term}")

Auto-Shutdown for Dev/Test

// Auto-shutdown schedule
resource autoShutdown 'Microsoft.DevTestLab/schedules@2018-09-15' = {
  name: 'shutdown-computevm-${vm.name}'
  location: location
  properties: {
    status: 'Enabled'
    taskType: 'ComputeVmShutdownTask'
    dailyRecurrence: {
      time: '1900'
    }
    timeZoneId: 'Pacific Standard Time'
    targetResourceId: vm.id
    notificationSettings: {
      status: 'Enabled'
      timeInMinutes: 30
      emailRecipient: 'team@yourorg.com'
    }
  }
}

Cost Alerts

resource budgetAlert 'Microsoft.Consumption/budgets@2021-10-01' = {
  name: 'budget-${environment}'
  properties: {
    category: 'Cost'
    amount: 10000
    timeGrain: 'Monthly'
    timePeriod: {
      startDate: '2024-01-01'
    }
    filter: {
      dimensions: {
        name: 'ResourceGroup'
        values: [resourceGroup().name]
      }
    }
    notifications: {
      actual_80_percent: {
        enabled: true
        operator: 'GreaterThanOrEqualTo'
        threshold: 80
        contactEmails: ['finance@yourorg.com', 'infra@yourorg.com']
      }
      forecasted_100_percent: {
        enabled: true
        operator: 'GreaterThanOrEqualTo'
        threshold: 100
        thresholdType: 'Forecasted'
        contactEmails: ['finance@yourorg.com', 'infra@yourorg.com']
      }
    }
  }
}

Monitoring and Diagnostics

Azure Monitor Configuration

// Log Analytics Workspace
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = {
  name: 'log-${environment}'
  location: location
  properties: {
    sku: {
      name: 'PerGB2018'
    }
    retentionInDays: 90
    features: {
      enableLogAccessUsingOnlyResourcePermissions: true
    }
  }
}

// Diagnostic settings for VMs
resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: 'diag-vm'
  scope: vm
  properties: {
    workspaceId: logAnalytics.id
    logs: []
    metrics: [
      {
        category: 'AllMetrics'
        enabled: true
        retentionPolicy: {
          days: 30
          enabled: true
        }
      }
    ]
  }
}

Lessons Learned

  1. Start with networking right. Changing VNet architecture later is painful.
  2. Use managed identities everywhere. Stop storing service principal secrets.
  3. Private endpoints are worth the complexity. Keep data off the internet.
  4. Reserved instances need planning. Buy RIs after you understand your baseline.
  5. Tagging is essential. You can’t manage costs without proper tagging.

Conclusion

Azure infrastructure requires both breadth and depth—understanding which services to use and how to configure them correctly. Start with proven patterns, automate everything with Bicep, and iterate based on real-world monitoring data.

The investment in proper architecture pays dividends: fewer security incidents, predictable costs, and infrastructure that scales with your needs.

Resources