Skip to content
Back to Blog
1 min read

Azure Well-Architected Reviews: Building Better Workloads

I wrote “Azure Well-Architected Reviews: Building Better Workloads” to share practical, production-minded guidance on this topic.

The Five Pillars

  1. Reliability - Resiliency and availability
  2. Security - Protect against threats
  3. Cost Optimization - Manage costs effectively
  4. Operational Excellence - Operations and monitoring
  5. Performance Efficiency - Scale to meet demand

Conducting a Well-Architected Assessment

Use the Well-Architected Review tool:

# Install Az.Advisor module
Install-Module -Name Az.Advisor -Force

# Get Well-Architected recommendations
$recommendations = Get-AzAdvisorRecommendation |
    Where-Object { $_.Category -in @('HighAvailability', 'Security', 'Performance', 'Cost') }

# Group by category
$grouped = $recommendations | Group-Object Category

foreach ($group in $grouped) {
    Write-Host "`n=== $($group.Name) ===" -ForegroundColor Cyan
    foreach ($rec in $group.Group) {
        Write-Host "  - $($rec.ShortDescription.Problem)"
        Write-Host "    Impact: $($rec.Impact)"
        Write-Host "    Resource: $($rec.ResourceMetadata.ResourceId)"
    }
}

Reliability Pillar

Implement resilient patterns:

// reliability/multi-region-deployment.bicep
param primaryLocation string = 'australiaeast'
param secondaryLocation string = 'australiasoutheast'

// Primary region deployment
module primaryRegion 'modules/app-deployment.bicep' = {
  name: 'primary-deployment'
  params: {
    location: primaryLocation
    isPrimary: true
  }
}

// Secondary region deployment
module secondaryRegion 'modules/app-deployment.bicep' = {
  name: 'secondary-deployment'
  params: {
    location: secondaryLocation
    isPrimary: false
  }
}

// Traffic Manager for failover
resource trafficManager 'Microsoft.Network/trafficManagerProfiles@2018-08-01' = {
  name: 'tm-global-app'
  location: 'global'
  properties: {
    profileStatus: 'Enabled'
    trafficRoutingMethod: 'Priority'
    dnsConfig: {
      relativeName: 'myapp-global'
      ttl: 60
    }
    monitorConfig: {
      protocol: 'HTTPS'
      port: 443
      path: '/health'
      intervalInSeconds: 30
      timeoutInSeconds: 10
      toleratedNumberOfFailures: 3
    }
    endpoints: [
      {
        name: 'primary'
        type: 'Microsoft.Network/trafficManagerProfiles/azureEndpoints'
        properties: {
          priority: 1
          targetResourceId: primaryRegion.outputs.appServiceId
          endpointStatus: 'Enabled'
        }
      }
      {
        name: 'secondary'
        type: 'Microsoft.Network/trafficManagerProfiles/azureEndpoints'
        properties: {
          priority: 2
          targetResourceId: secondaryRegion.outputs.appServiceId
          endpointStatus: 'Enabled'
        }
      }
    ]
  }
}

Security Pillar

Implement defense in depth:

// security/defense-in-depth.bicep
param location string

// Network security
resource nsg 'Microsoft.Network/networkSecurityGroups@2021-08-01' = {
  name: 'nsg-app-tier'
  location: location
  properties: {
    securityRules: [
      {
        name: 'AllowHttpsInbound'
        properties: {
          priority: 100
          direction: 'Inbound'
          access: 'Allow'
          protocol: 'Tcp'
          sourceAddressPrefix: 'Internet'
          sourcePortRange: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '443'
        }
      }
      {
        name: 'DenyAllInbound'
        properties: {
          priority: 4096
          direction: 'Inbound'
          access: 'Deny'
          protocol: '*'
          sourceAddressPrefix: '*'
          sourcePortRange: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '*'
        }
      }
    ]
  }
}

// Key Vault for secrets
resource keyVault 'Microsoft.KeyVault/vaults@2021-11-01-preview' = {
  name: 'kv-app-secrets'
  location: location
  properties: {
    sku: {
      family: 'A'
      name: 'standard'
    }
    tenantId: subscription().tenantId
    enableRbacAuthorization: true
    enableSoftDelete: true
    softDeleteRetentionInDays: 90
    enablePurgeProtection: true
    networkAcls: {
      defaultAction: 'Deny'
      bypass: 'AzureServices'
    }
  }
}

// Diagnostic settings for audit
resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  scope: keyVault
  name: 'security-audit'
  properties: {
    workspaceId: logAnalytics.id
    logs: [
      {
        category: 'AuditEvent'
        enabled: true
        retentionPolicy: {
          enabled: true
          days: 365
        }
      }
    ]
  }
}

Cost Optimization Pillar

# Cost analysis script
$costData = Get-AzConsumptionUsageDetail -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date)

# Identify cost drivers
$topResources = $costData |
    Group-Object InstanceId |
    ForEach-Object {
        [PSCustomObject]@{
            Resource = $_.Name
            TotalCost = ($_.Group | Measure-Object -Property PretaxCost -Sum).Sum
        }
    } |
    Sort-Object TotalCost -Descending |
    Select-Object -First 10

# Recommendations
$topResources | ForEach-Object {
    $resource = Get-AzResource -ResourceId $_.Resource -ErrorAction SilentlyContinue
    if ($resource) {
        $advisor = Get-AzAdvisorRecommendation |
            Where-Object { $_.ResourceMetadata.ResourceId -eq $_.Resource -and $_.Category -eq 'Cost' }

        [PSCustomObject]@{
            ResourceName = $resource.Name
            ResourceType = $resource.ResourceType
            MonthlyCost = [math]::Round($_.TotalCost, 2)
            Recommendations = $advisor.ShortDescription.Problem -join '; '
        }
    }
}

Assessment Automation

# .github/workflows/well-architected-review.yml
name: Well-Architected Assessment

on:
  schedule:
    - cron: '0 0 1 * *'  # Monthly
  workflow_dispatch:

jobs:
  assess:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Azure Login
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Run Assessment
        run: |
          # Get Advisor recommendations
          az advisor recommendation list \
            --category HighAvailability Security Performance Cost \
            --output json > assessment-results.json

          # Generate report
          python scripts/generate-war-report.py assessment-results.json

      - name: Upload Report
        uses: actions/upload-artifact@v3
        with:
          name: well-architected-report
          path: well-architected-report.html

      - name: Create Issue for High Impact Items
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('fs');
            const results = JSON.parse(fs.readFileSync('assessment-results.json'));
            const highImpact = results.filter(r => r.impact === 'High');

            if (highImpact.length > 0) {
              const body = highImpact.map(r =>
                `- **${r.category}**: ${r.shortDescription.problem}`
              ).join('\n');

              await github.rest.issues.create({
                owner: context.repo.owner,
                repo: context.repo.repo,
                title: 'Well-Architected Review: High Impact Recommendations',
                body: `## Monthly Assessment\n\n${body}`,
                labels: ['well-architected', 'infrastructure']
              });
            }

Regular Well-Architected Reviews ensure your workloads remain optimized across all five pillars as they evolve.\n\n## Takeaways\n\nAdd a concise, personal takeaway and recommended next steps here.\n

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.