
Azure Cloud Architect
- 582 installs
- 23.5k repo stars
- Updated July 17, 2026
- alirezarezvani/claude-skills
azure-cloud-architect is a Claude Code skill that designs cost-bounded Azure stacks, Bicep and ARM templates, and DevOps deployment paths for developers planning AKS, App Service, Functions, or Cosmos DB before committin
About
azure-cloud-architect is a Claude Code skill from alirezarezvani/claude-skills that designs scalable, cost-effective Azure architectures with Bicep infrastructure-as-code templates. The workflow gathers application type, expected users, requests per second, and compliance needs, then recommends services including AKS, App Service, Azure Functions, and Cosmos DB with cost optimization guidance. Developers reach for azure-cloud-architect when migrating workloads to Azure, drafting IaC before provisioning, or setting up Azure DevOps pipelines. The skill covers startup and enterprise scenarios, producing architecture decisions and Bicep shapes rather than running live deployments.
- Structured requirements pass: app type, RPS, budget cap, compliance (GDPR, HIPAA, SOC 2, ISO 27001), RPO/RTO, and region
- architecture_designer.py CLI recommends patterns, service stacks, pros/cons, and estimated monthly USD cost
- Covers AKS, App Service, Azure Functions, Cosmos DB, Front Door, Key Vault, and Entra ID in example stacks
- Explicit workflow from requirements → architecture design → Bicep infrastructure-as-code templates
- Oriented to startups and enterprises migrating or greenfielding on Azure with cost optimization
Azure Cloud Architect by the numbers
- 582 all-time installs (skills.sh)
- Ranked #349 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
npx skills add https://github.com/alirezarezvani/claude-skills --skill azure-cloud-architectAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 582 |
|---|---|
| repo stars | ★ 23.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 17, 2026 |
| Repository | alirezarezvani/claude-skills ↗ |
How do you design a cost-bounded Azure architecture in Bicep?
Design cost-bounded Azure stacks, Bicep/ARM shapes, and DevOps paths before you commit spend on AKS, App Service, Functions, or Cosmos DB.
Who is it for?
Platform engineers and architects planning Azure migrations or greenfield deployments who need Bicep templates and cost-bounded service selection.
Skip if: Teams on AWS or GCP only, or developers needing live Terraform apply and runtime monitoring should use cloud-specific ops tools instead.
When should I use this skill?
A developer asks to design Azure infrastructure, create Bicep templates, optimize Azure costs, or plan an Azure DevOps migration.
What you get
Azure architecture recommendation with Bicep or ARM templates, service selection, and cost-optimized DevOps pipeline plan.
- Bicep or ARM template drafts
- Azure architecture recommendation document
Files
Azure Cloud Architect
Design scalable, cost-effective Azure architectures for startups and enterprises with Bicep infrastructure-as-code templates.
---
Workflow
Step 1: Gather Requirements
Collect application specifications:
- Application type (web app, mobile backend, data pipeline, SaaS, microservices)
- Expected users and requests per second
- Budget constraints (monthly spend limit)
- Team size and Azure experience level
- Compliance requirements (GDPR, HIPAA, SOC 2, ISO 27001)
- Availability requirements (SLA, RPO/RTO)
- Region preferences (data residency, latency)Step 2: Design Architecture
Run the architecture designer to get pattern recommendations:
python scripts/architecture_designer.py \
--app-type web_app \
--users 10000 \
--requirements '{"budget_monthly_usd": 500, "compliance": ["SOC2"]}'Example output:
{
"recommended_pattern": "app_service_web",
"service_stack": ["App Service", "Azure SQL", "Front Door", "Key Vault", "Entra ID"],
"estimated_monthly_cost_usd": 280,
"pros": ["Managed platform", "Built-in autoscale", "Deployment slots"],
"cons": ["Less control than VMs", "Platform constraints", "Cold start on consumption plans"]
}Select from recommended patterns:
- App Service Web: Front Door + App Service + Azure SQL + Redis Cache
- Microservices on AKS: AKS + Service Bus + Cosmos DB + API Management
- Serverless Event-Driven: Functions + Event Grid + Service Bus + Cosmos DB
- Data Pipeline: Data Factory + Synapse Analytics + Data Lake Storage + Event Hubs
See references/architecture_patterns.md for detailed pattern specifications.
Validation checkpoint: Confirm the recommended pattern matches the team's operational maturity and compliance requirements before proceeding to Step 3.
Step 3: Generate IaC Templates
Create infrastructure-as-code for the selected pattern:
# Web app stack (Bicep)
python scripts/bicep_generator.py --arch-type web-app --output main.bicepExample Bicep output (core web app resources):
@description('The environment name')
param environment string = 'dev'
@description('The Azure region for resources')
param location string = resourceGroup().location
@description('The application name')
param appName string = 'myapp'
// App Service Plan
resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = {
name: '${environment}-${appName}-plan'
location: location
sku: {
name: 'P1v3'
tier: 'PremiumV3'
capacity: 1
}
properties: {
reserved: true // Linux
}
}
// App Service
resource appService 'Microsoft.Web/sites@2023-01-01' = {
name: '${environment}-${appName}-web'
location: location
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true
siteConfig: {
linuxFxVersion: 'NODE|20-lts'
minTlsVersion: '1.2'
ftpsState: 'Disabled'
alwaysOn: true
}
}
identity: {
type: 'SystemAssigned'
}
}
// Azure SQL Database
resource sqlServer 'Microsoft.Sql/servers@2023-05-01-preview' = {
name: '${environment}-${appName}-sql'
location: location
properties: {
administrators: {
azureADOnlyAuthentication: true
}
minimalTlsVersion: '1.2'
}
}
resource sqlDatabase 'Microsoft.Sql/servers/databases@2023-05-01-preview' = {
parent: sqlServer
name: '${appName}-db'
location: location
sku: {
name: 'GP_S_Gen5_2'
tier: 'GeneralPurpose'
}
properties: {
autoPauseDelay: 60
minCapacity: json('0.5')
}
}Full templates including Front Door, Key Vault, Managed Identity, and monitoring are generated bybicep_generator.pyand also available inreferences/architecture_patterns.md.
Bicep is the recommended IaC language for Azure. Prefer Bicep over ARM JSON templates: Bicep compiles to ARM JSON, has cleaner syntax, supports modules, and is first-party supported by Microsoft.
Step 4: Review Costs
Analyze estimated costs and optimization opportunities:
python scripts/cost_optimizer.py \
--config current_resources.json \
--jsonExample output:
{
"current_monthly_usd": 2000,
"recommendations": [
{ "action": "Right-size SQL Database GP_S_Gen5_8 to GP_S_Gen5_2", "savings_usd": 380, "priority": "high" },
{ "action": "Purchase 1-year Reserved Instances for AKS node pools", "savings_usd": 290, "priority": "high" },
{ "action": "Move Blob Storage to Cool tier for objects >30 days old", "savings_usd": 65, "priority": "medium" }
],
"total_potential_savings_usd": 735
}Output includes:
- Monthly cost breakdown by service
- Right-sizing recommendations
- Reserved Instance and Savings Plan opportunities
- Potential monthly savings
Step 5: Configure CI/CD
Set up Azure DevOps Pipelines or GitHub Actions with Azure:
# GitHub Actions — deploy Bicep to Azure
name: Deploy Infrastructure
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- uses: azure/arm-deploy@v2
with:
resourceGroupName: rg-myapp-dev
template: ./infra/main.bicep
parameters: environment=dev# Azure DevOps Pipeline
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'MyServiceConnection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az deployment group create \
--resource-group rg-myapp-dev \
--template-file infra/main.bicep \
--parameters environment=devStep 6: Security Review
Validate security posture before production:
- Identity: Entra ID (Azure AD) with RBAC, Managed Identity for service-to-service auth — never store credentials in code
- Secrets: Key Vault for all secrets, certificates, and connection strings
- Network: NSGs on all subnets, Private Endpoints for PaaS services, Application Gateway with WAF
- Encryption: TLS 1.2+ in transit, Azure-managed or customer-managed keys at rest
- Monitoring: Microsoft Defender for Cloud enabled, Azure Policy for guardrails
- Compliance: Azure Policy assignments for SOC 2 / HIPAA / ISO 27001 initiatives
If deployment fails:
1. Check the deployment status:
az deployment group show \
--resource-group rg-myapp-dev \
--name main \
--query 'properties.error'2. Review Activity Log for RBAC or policy errors. 3. Validate the Bicep template before deploying:
az bicep build --file main.bicep
az deployment group validate \
--resource-group rg-myapp-dev \
--template-file main.bicepCommon failure causes:
- RBAC permission errors — verify the deploying principal has Contributor on the resource group
- Resource provider not registered — run
az provider register --namespace Microsoft.Web - Naming conflicts — Azure resource names are often globally unique (storage accounts, web apps)
- Quota exceeded — request quota increase via Azure Portal > Subscriptions > Usage + quotas
---
Tools
architecture_designer.py
Generates architecture pattern recommendations based on requirements.
python scripts/architecture_designer.py \
--app-type web_app \
--users 50000 \
--requirements '{"budget_monthly_usd": 1000, "compliance": ["HIPAA"]}' \
--jsonInput: Application type, expected users, JSON requirements Output: Recommended pattern, service stack, cost estimate, pros/cons
cost_optimizer.py
Analyzes Azure resource configurations for cost savings.
python scripts/cost_optimizer.py --config resources.json --jsonInput: JSON file with current Azure resource inventory Output: Recommendations for:
- Idle resource removal
- VM and database right-sizing
- Reserved Instance purchases
- Storage tier transitions
- Unused public IPs and load balancers
bicep_generator.py
Generates Bicep template scaffolds from architecture type.
python scripts/bicep_generator.py --arch-type microservices --output main.bicepOutput: Production-ready Bicep templates with:
- Managed Identity (no passwords)
- Key Vault integration
- Diagnostic settings for Azure Monitor
- Network security groups
- Tags for cost allocation
---
Quick Start
Web App Architecture (< $100/month)
Ask: "Design an Azure web app for a startup with 5000 users"
Result:
- App Service (B1 Linux) for the application
- Azure SQL Serverless for relational data
- Azure Blob Storage for static assets
- Front Door (free tier) for CDN and routing
- Key Vault for secrets
- Estimated: $40-80/monthMicroservices on AKS ($500-2000/month)
Ask: "Design a microservices architecture on Azure for a SaaS platform with 50k users"
Result:
- AKS cluster with 3 node pools (system, app, jobs)
- API Management for gateway and rate limiting
- Cosmos DB for multi-model data
- Service Bus for async messaging
- Azure Monitor + Application Insights for observability
- Multi-zone deploymentServerless Event-Driven (< $200/month)
Ask: "Design an event-driven backend for processing orders"
Result:
- Azure Functions (Consumption plan) for compute
- Event Grid for event routing
- Service Bus for reliable messaging
- Cosmos DB for order data
- Application Insights for monitoring
- Estimated: $30-150/month depending on volumeData Pipeline ($300-1500/month)
Ask: "Design a data pipeline for ingesting 10M events/day"
Result:
- Event Hubs for ingestion
- Stream Analytics or Functions for processing
- Data Lake Storage Gen2 for raw data
- Synapse Analytics for warehouse
- Power BI for dashboards---
Input Requirements
Provide these details for architecture design:
| Requirement | Description | Example |
|---|---|---|
| Application type | What you're building | SaaS platform, mobile backend |
| Expected scale | Users, requests/sec | 10k users, 100 RPS |
| Budget | Monthly Azure limit | $500/month max |
| Team context | Size, Azure experience | 3 devs, intermediate |
| Compliance | Regulatory needs | HIPAA, GDPR, SOC 2 |
| Availability | Uptime requirements | 99.9% SLA, 1hr RPO |
JSON Format:
{
"application_type": "saas_platform",
"expected_users": 10000,
"requests_per_second": 100,
"budget_monthly_usd": 500,
"team_size": 3,
"azure_experience": "intermediate",
"compliance": ["SOC2"],
"availability_sla": "99.9%"
}---
Anti-Patterns
| Anti-Pattern | Why It Fails | Do This Instead |
|---|---|---|
| ARM JSON templates for new projects | Verbose, hard to read, no modules | Use Bicep — compiles to ARM, cleaner syntax |
| Storing secrets in App Settings | Secrets visible in portal, no rotation | Use Key Vault references in App Settings |
| Single large AKS node pool | Cannot optimize for different workloads | Use multiple node pools: system, app, jobs |
| Public endpoints on PaaS services | Exposed attack surface | Use Private Endpoints + VNet integration |
| Over-provisioning "just in case" | Wastes budget month one | Start small, use autoscale, right-size monthly |
| Shared resource groups for everything | Blast radius, RBAC nightmares | One resource group per environment per workload |
| No tagging strategy | Cannot track costs or ownership | Tag: environment, owner, cost-center, app-name |
| Using classic resources | Deprecated, limited features | Use ARM/Bicep resources exclusively |
---
Output Formats
Architecture Design
- Pattern recommendation with rationale
- Service stack diagram (ASCII)
- Monthly cost estimate and trade-offs
IaC Templates
- Bicep: Recommended — first-party, module support, clean syntax
- ARM JSON: Generated from Bicep when needed
- Terraform HCL: Multi-cloud compatible using azurerm provider
Cost Analysis
- Current spend breakdown with optimization recommendations
- Priority action list (high/medium/low) and implementation checklist
---
Cross-References
| Skill | Relationship |
|---|---|
engineering-team/aws-solution-architect | AWS equivalent — same 6-step workflow, different services |
engineering-team/gcp-cloud-architect | GCP equivalent — completes the cloud trifecta |
engineering-team/senior-devops | Broader DevOps scope — pipelines, monitoring, containerization |
engineering/terraform-patterns | IaC implementation — use for Terraform modules targeting Azure |
engineering/ci-cd-pipeline-builder | Pipeline construction — automates Azure DevOps and GitHub Actions |
---
Reference Documentation
| Document | Contents |
|---|---|
references/architecture_patterns.md | 5 patterns: web app, microservices/AKS, serverless, data pipeline, multi-region |
references/service_selection.md | Decision matrices for compute, database, storage, messaging, networking |
references/best_practices.md | Naming conventions, tagging, RBAC, network security, monitoring, DR |
Azure Architecture Patterns
Reference guide for selecting the right Azure architecture pattern based on application requirements.
---
Table of Contents
- Pattern Selection Matrix
- Pattern 1: App Service Web Application
- Pattern 2: Microservices on AKS
- Pattern 3: Serverless Event-Driven
- Pattern 4: Data Pipeline
- Pattern 5: Multi-Region Active-Active
- Well-Architected Framework Alignment
---
Pattern Selection Matrix
| Pattern | Best For | Users | Monthly Cost | Complexity |
|---|---|---|---|---|
| App Service Web | MVPs, SaaS, APIs | <100K | $50-500 | Low |
| Microservices on AKS | Complex platforms, multi-team | Any | $500-5000 | High |
| Serverless Event-Driven | Event processing, webhooks, APIs | <1M | $20-500 | Low-Medium |
| Data Pipeline | Analytics, ETL, ML | Any | $200-3000 | Medium-High |
| Multi-Region Active-Active | Global apps, 99.99% uptime | >100K | 1.5-2x single | High |
---
Pattern 1: App Service Web Application
Architecture
┌──────────────┐
│ Azure Front │
│ Door │
│ (CDN + WAF) │
└──────┬───────┘
│
┌──────▼───────┐
│ App Service │
│ (Linux P1v3)│
│ + Slots │
└──┬───────┬───┘
│ │
┌────────▼──┐ ┌──▼────────┐
│ Azure SQL │ │ Blob │
│ Serverless │ │ Storage │
└────────────┘ └───────────┘
│
┌────────▼──────────┐
│ Key Vault │
│ (secrets, certs) │
└───────────────────┘Services
| Service | Purpose | Configuration |
|---|---|---|
| Azure Front Door | Global CDN, WAF, SSL | Standard or Premium tier, custom domain |
| App Service | Web application hosting | Linux P1v3 (production), B1 (dev) |
| Azure SQL Database | Relational database | Serverless GP_S_Gen5_2 with auto-pause |
| Blob Storage | Static assets, uploads | Hot tier with lifecycle policies |
| Key Vault | Secrets management | RBAC authorization, soft-delete enabled |
| Application Insights | Monitoring and APM | Workspace-based, connected to Log Analytics |
| Entra ID | Authentication | Easy Auth or MSAL library |
Deployment Strategy
- Deployment slots: staging slot for zero-downtime deploys, swap to production after validation
- Auto-scale: CPU-based rules, 1-10 instances in production
- Health checks:
/healthendpoint monitored by App Service and Front Door
Cost Estimate
| Component | Dev | Production |
|---|---|---|
| App Service | $13 (B1) | $75 (P1v3) |
| Azure SQL | $5 (Basic) | $40-120 (Serverless GP) |
| Front Door | $0 (disabled) | $35-55 |
| Blob Storage | $1 | $5-15 |
| Key Vault | $0.03 | $1-5 |
| Application Insights | $0 (free tier) | $5-20 |
| Total | ~$19 | ~$160-290 |
---
Pattern 2: Microservices on AKS
Architecture
┌──────────────┐
│ Azure Front │
│ Door │
└──────┬───────┘
│
┌──────▼───────┐
│ API Mgmt │
│ (gateway) │
└──────┬───────┘
│
┌────────────▼────────────┐
│ AKS Cluster │
│ ┌───────┐ ┌───────┐ │
│ │ svc-A │ │ svc-B │ │
│ └───┬───┘ └───┬───┘ │
│ │ │ │
│ ┌───▼─────────▼───┐ │
│ │ Service Bus │ │
│ │ (async msgs) │ │
│ └─────────────────┘ │
└─────────────────────────┘
│ │
┌────────▼──┐ ┌──▼────────┐
│ Cosmos DB │ │ ACR │
│ (data) │ │ (images) │
└────────────┘ └───────────┘Services
| Service | Purpose | Configuration |
|---|---|---|
| AKS | Container orchestration | 3 node pools: system (D2s_v5), app (D4s_v5), jobs (spot) |
| API Management | API gateway, rate limiting | Standard v2 or Consumption tier |
| Cosmos DB | Multi-model database | Session consistency, autoscale RU/s |
| Service Bus | Async messaging | Standard tier, topics for pub/sub |
| Container Registry | Docker image storage | Basic (dev), Standard (prod) |
| Key Vault | Secrets for pods | CSI driver + workload identity |
| Azure Monitor | Cluster and app observability | Container Insights + App Insights |
AKS Best Practices
Node Pools:
- System pool: 2-3 nodes, D2s_v5, taints for system pods only
- App pool: 2-10 nodes (autoscaler), D4s_v5, for application workloads
- Jobs pool: spot instances, for batch processing and CI runners
Networking:
- Azure CNI for VNet-native pod networking
- Network policies (Azure or Calico) for pod-to-pod isolation
- Ingress via NGINX Ingress Controller or Application Gateway Ingress Controller (AGIC)
Security:
- Workload Identity for pod-to-Azure service auth (replaces pod identity)
- Azure Policy for Kubernetes (OPA Gatekeeper)
- Defender for Containers for runtime threat detection
- Private cluster for production (API server not exposed to internet)
Deployment:
- Helm charts for application packaging
- Flux or ArgoCD for GitOps
- Horizontal Pod Autoscaler (HPA) + KEDA for event-driven scaling
Cost Estimate
| Component | Dev | Production |
|---|---|---|
| AKS nodes (system) | $60 (1x D2s_v5) | $180 (3x D2s_v5) |
| AKS nodes (app) | $120 (1x D4s_v5) | $360 (3x D4s_v5) |
| API Management | $0 (Consumption) | $175 (Standard v2) |
| Cosmos DB | $25 (serverless) | $100-400 (autoscale) |
| Service Bus | $10 | $10-50 |
| Container Registry | $5 | $20 |
| Monitoring | $0 | $50-100 |
| Total | ~$220 | ~$900-1300 |
---
Pattern 3: Serverless Event-Driven
Architecture
┌──────────┐ ┌──────────┐ ┌──────────┐
│ HTTP │ │ Blob │ │ Timer │
│ Trigger │ │ Trigger │ │ Trigger │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└────────┬───────┘─────────┬───────┘
│ │
┌──────▼───────┐ ┌──────▼───────┐
│ Azure │ │ Azure │
│ Functions │ │ Functions │
│ (handlers) │ │ (workers) │
└──┬────┬──────┘ └──────┬───────┘
│ │ │
┌─────────▼┐ ┌─▼──────────┐ ┌─▼──────────┐
│ Event │ │ Service │ │ Cosmos DB │
│ Grid │ │ Bus Queue │ │ (data) │
│ (fanout) │ │ (reliable) │ │ │
└──────────┘ └────────────┘ └────────────┘Services
| Service | Purpose | Configuration |
|---|---|---|
| Azure Functions | Event handlers, APIs | Consumption plan (dev), Premium (prod) |
| Event Grid | Event routing and fan-out | System + custom topics |
| Service Bus | Reliable messaging with DLQ | Basic or Standard, queues + topics |
| Cosmos DB | Low-latency data store | Serverless (dev), autoscale (prod) |
| Blob Storage | File processing triggers | Lifecycle policies |
| Application Insights | Function monitoring | Sampling at 5-10% for high volume |
Durable Functions Patterns
Use Durable Functions for orchestration instead of building custom state machines:
| Pattern | Use Case | Example |
|---|---|---|
| Function chaining | Sequential steps | Order: validate -> charge -> fulfill -> notify |
| Fan-out/fan-in | Parallel processing | Process all images in a batch, aggregate results |
| Async HTTP APIs | Long-running operations | Start job, poll for status, return result |
| Monitor | Periodic polling | Check external API until condition met |
| Human interaction | Approval workflows | Send approval email, wait for response with timeout |
Cost Estimate
| Component | Dev | Production |
|---|---|---|
| Functions (Consumption) | $0 (1M free) | $5-30 |
| Event Grid | $0 | $0-5 |
| Service Bus | $0 (Basic) | $10-30 |
| Cosmos DB | $0 (serverless free tier) | $25-150 |
| Blob Storage | $1 | $5-15 |
| Application Insights | $0 | $5-15 |
| Total | ~$1 | ~$50-245 |
---
Pattern 4: Data Pipeline
Architecture
┌──────────┐ ┌──────────┐
│ IoT/Apps │ │ Batch │
│ (events) │ │ (files) │
└────┬─────┘ └────┬─────┘
│ │
┌────▼─────┐ ┌────▼─────┐
│ Event │ │ Data │
│ Hubs │ │ Factory │
└────┬─────┘ └────┬─────┘
│ │
└────────┬───────┘
│
┌────────▼────────┐
│ Data Lake │
│ Storage Gen2 │
│ (raw/curated) │
└────────┬────────┘
│
┌────────▼────────┐
│ Synapse │
│ Analytics │
│ (SQL + Spark) │
└────────┬────────┘
│
┌────────▼────────┐
│ Power BI │
│ (dashboards) │
└─────────────────┘Services
| Service | Purpose | Configuration |
|---|---|---|
| Event Hubs | Real-time event ingestion | Standard, 2-8 partitions |
| Data Factory | Batch ETL orchestration | Managed, 90+ connectors |
| Data Lake Storage Gen2 | Raw and curated data lake | HNS enabled, lifecycle policies |
| Synapse Analytics | SQL and Spark analytics | Serverless SQL pool (pay-per-query) |
| Azure Functions | Lightweight processing | Triggered by Event Hubs or Blob |
| Power BI | Business intelligence | Pro ($10/user/month) |
Data Lake Organization
data-lake/
├── raw/ # Landing zone — immutable source data
│ ├── source-system-a/
│ │ └── YYYY/MM/DD/ # Date-partitioned
│ └── source-system-b/
├── curated/ # Cleaned, validated, business-ready
│ ├── dimension/
│ └── fact/
├── sandbox/ # Ad-hoc exploration
└── archive/ # Cold storage (lifecycle policy target)Cost Estimate
| Component | Dev | Production |
|---|---|---|
| Event Hubs (1 TU) | $22 | $44-176 |
| Data Factory | $0 (free tier) | $50-200 |
| Data Lake Storage | $5 | $20-80 |
| Synapse Serverless SQL | $5 | $50-300 |
| Azure Functions | $0 | $5-20 |
| Power BI Pro | $10/user | $10/user |
| Total | ~$42 | ~$180-800 |
---
Pattern 5: Multi-Region Active-Active
Architecture
┌──────────────┐
│ Azure Front │
│ Door (Global│
│ LB + WAF) │
└──┬────────┬──┘
│ │
┌──────────▼──┐ ┌──▼──────────┐
│ Region 1 │ │ Region 2 │
│ (East US) │ │ (West EU) │
│ │ │ │
│ App Service │ │ App Service │
│ + SQL │ │ + SQL │
│ + Redis │ │ + Redis │
└──────┬──────┘ └──────┬──────┘
│ │
┌──────▼───────────────▼──────┐
│ Cosmos DB │
│ (multi-region writes) │
│ Session consistency │
└─────────────────────────────┘Multi-Region Design Decisions
| Decision | Recommendation | Rationale |
|---|---|---|
| Global load balancer | Front Door Premium | Built-in WAF, CDN, health probes, fastest failover |
| Database replication | Cosmos DB multi-write or SQL failover groups | Cosmos for global writes, SQL for relational needs |
| Session state | Azure Cache for Redis (per region) | Local sessions, avoid cross-region latency |
| Static content | Front Door CDN | Edge-cached, no origin required |
| DNS strategy | Front Door handles routing | No separate Traffic Manager needed |
| Failover | Automatic (Front Door health probes) | 10-30 second detection, automatic reroute |
Azure SQL Failover Groups vs Cosmos DB Multi-Region
| Feature | SQL Failover Groups | Cosmos DB Multi-Region |
|---|---|---|
| Replication | Async (RPO ~5s) | Sync or async (configurable) |
| Write region | Single primary | Multi-write capable |
| Failover | Automatic or manual (60s grace) | Automatic |
| Consistency | Strong (single writer) | 5 levels (session recommended) |
| Cost | 2x compute (active-passive) | Per-region RU/s charge |
| Best for | Relational data, transactions | Document data, global low-latency |
Cost Impact
Multi-region typically costs 1.5-2x single region:
- Compute: 2x (running in both regions)
- Database: 1.5-2x (replication, multi-write)
- Networking: Additional cross-region data transfer (~$0.02-0.05/GB)
- Front Door Premium: ~$100-200/month
---
Well-Architected Framework Alignment
Every architecture pattern should address all five pillars of the Azure Well-Architected Framework.
Reliability
- Deploy across Availability Zones (zone-redundant App Service, AKS, SQL)
- Enable health probes at every layer
- Implement retry policies with exponential backoff (Polly for .NET, tenacity for Python)
- Define RPO/RTO and test disaster recovery quarterly
- Use Azure Chaos Studio for fault injection testing
Security
- Entra ID for all human and service authentication
- Managed Identity for all Azure service-to-service communication
- Key Vault for secrets, certificates, and encryption keys — no secrets in code or config
- Private Endpoints for all PaaS services in production
- Microsoft Defender for Cloud for threat detection and compliance
Cost Optimization
- Use serverless and consumption-based services where possible
- Auto-pause Azure SQL in dev/test (serverless tier)
- Spot VMs for fault-tolerant AKS node pools
- Reserved Instances for steady-state production workloads (1-year = 35% savings)
- Azure Advisor cost recommendations — review weekly
- Set budgets and alerts at subscription and resource group level
Operational Excellence
- Bicep for all infrastructure (no manual portal deployments)
- GitOps for AKS (Flux or ArgoCD)
- Deployment slots or blue-green for zero-downtime deploys
- Centralized logging in Log Analytics with standardized KQL queries
- Azure DevOps or GitHub Actions for CI/CD with workload identity federation
Performance Efficiency
- Application Insights for distributed tracing and performance profiling
- Azure Cache for Redis for session state and hot-path caching
- Front Door for edge caching and global acceleration
- Autoscale rules on compute (CPU, memory, HTTP queue length)
- Load testing with Azure Load Testing before production launch
Azure Best Practices
Production-ready practices for naming, tagging, security, networking, monitoring, and disaster recovery on Azure.
---
Table of Contents
- Naming Conventions
- Tagging Strategy
- RBAC and Least Privilege
- Network Security
- Monitoring and Alerting
- Disaster Recovery
- Common Pitfalls
---
Naming Conventions
Follow the Azure Cloud Adoption Framework (CAF) naming convention for consistency and automation.
Format
<resource-type>-<workload>-<environment>-<region>-<instance>Examples
| Resource | Naming Pattern | Example |
|---|---|---|
| Resource Group | rg-\<workload\>-\<env\> | rg-myapp-prod |
| App Service | app-\<workload\>-\<env\> | app-myapp-prod |
| App Service Plan | plan-\<workload\>-\<env\> | plan-myapp-prod |
| Azure SQL Server | sql-\<workload\>-\<env\> | sql-myapp-prod |
| Azure SQL Database | sqldb-\<workload\>-\<env\> | sqldb-myapp-prod |
| Storage Account | st\<workload\>\<env\> (no hyphens) | stmyappprod |
| Key Vault | kv-\<workload\>-\<env\> | kv-myapp-prod |
| AKS Cluster | aks-\<workload\>-\<env\> | aks-myapp-prod |
| Container Registry | cr\<workload\>\<env\> (no hyphens) | crmyappprod |
| Virtual Network | vnet-\<workload\>-\<env\> | vnet-myapp-prod |
| Subnet | snet-\<purpose\> | snet-app, snet-data |
| NSG | nsg-\<subnet-name\> | nsg-snet-app |
| Public IP | pip-\<resource\>-\<env\> | pip-agw-prod |
| Cosmos DB | cosmos-\<workload\>-\<env\> | cosmos-myapp-prod |
| Service Bus | sb-\<workload\>-\<env\> | sb-myapp-prod |
| Event Hubs | evh-\<workload\>-\<env\> | evh-myapp-prod |
| Log Analytics | log-\<workload\>-\<env\> | log-myapp-prod |
| Application Insights | ai-\<workload\>-\<env\> | ai-myapp-prod |
Rules
- Lowercase only (some resources require it — be consistent everywhere)
- Hyphens as separators (except where disallowed: storage accounts, container registries)
- No longer than the resource type max length (e.g., storage accounts max 24 characters)
- Environment abbreviations:
dev,stg,prod - Region abbreviations:
eus(East US),weu(West Europe),sea(Southeast Asia)
---
Tagging Strategy
Tags enable cost allocation, ownership tracking, and automation. Apply to every resource.
Required Tags
| Tag Key | Purpose | Example Values |
|---|---|---|
| environment | Cost splitting, policy targeting | dev, staging, production |
| app-name | Workload identification | myapp, data-pipeline |
| owner | Team or individual responsible | platform-team, jane.doe@company.com |
| cost-center | Finance allocation | CC-1234, engineering |
Recommended Tags
| Tag Key | Purpose | Example Values |
|---|---|---|
| created-by | IaC or manual tracking | bicep, terraform, portal |
| data-classification | Security posture | public, internal, confidential |
| compliance | Regulatory requirements | hipaa, gdpr, sox |
| auto-shutdown | Dev/test cost savings | true, false |
Enforcement
Use Azure Policy to enforce tagging:
{
"if": {
"allOf": [
{ "field": "tags['environment']", "exists": "false" },
{ "field": "type", "notEquals": "Microsoft.Resources/subscriptions/resourceGroups" }
]
},
"then": { "effect": "deny" }
}---
RBAC and Least Privilege
Principles
1. Use built-in roles before creating custom roles 2. Assign roles to groups, not individual users 3. Scope to the narrowest level — resource group or resource, not subscription 4. Use Managed Identity for service-to-service — never store credentials 5. Enable Entra ID PIM (Privileged Identity Management) for just-in-time admin access
Common Role Assignments
| Persona | Scope | Role |
|---|---|---|
| Developer | Resource Group (dev) | Contributor |
| Developer | Resource Group (prod) | Reader |
| CI/CD pipeline | Resource Group | Contributor (via workload identity) |
| App Service | Key Vault | Key Vault Secrets User |
| App Service | Azure SQL | SQL DB Contributor (or Entra auth) |
| AKS pod | Cosmos DB | Cosmos DB Built-in Data Contributor |
| Security team | Subscription | Security Reader |
| Platform team | Subscription | Owner (with PIM) |
Workload Identity Federation
For CI/CD pipelines (GitHub Actions, Azure DevOps), use workload identity federation instead of service principal secrets:
# Create federated credential (GitHub Actions example)
az ad app federated-credential create \
--id <app-object-id> \
--parameters '{
"name": "github-main",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:org/repo:ref:refs/heads/main",
"audiences": ["api://AzureADTokenExchange"]
}'---
Network Security
Defense in Depth
| Layer | Control | Implementation |
|---|---|---|
| Edge | DDoS + WAF | Azure DDoS Protection + Front Door WAF |
| Perimeter | Firewall | Azure Firewall or NVA for hub VNet |
| Network | Segmentation | VNet + subnets + NSGs |
| Application | Access control | Private Endpoints + Managed Identity |
| Data | Encryption | TLS 1.2+ in transit, CMK at rest |
Private Endpoints
Every PaaS service in production must use Private Endpoints:
| Service | Private Endpoint Support | Private DNS Zone |
|---|---|---|
| Azure SQL | Yes | privatelink.database.windows.net |
| Cosmos DB | Yes | privatelink.documents.azure.com |
| Key Vault | Yes | privatelink.vaultcore.azure.net |
| Storage (Blob) | Yes | privatelink.blob.core.windows.net |
| Container Registry | Yes | privatelink.azurecr.io |
| Service Bus | Yes | privatelink.servicebus.windows.net |
| App Service | VNet Integration (outbound) + Private Endpoint (inbound) | privatelink.azurewebsites.net |
NSG Rules Baseline
Every subnet should have an NSG. Start with deny-all inbound, then open only what is needed:
Priority Direction Action Source Destination Port
100 Inbound Allow Front Door App Subnet 443
200 Inbound Allow App Subnet Data Subnet 1433,5432
300 Inbound Allow VNet VNet Any (internal)
4096 Inbound Deny Any Any AnyApplication Gateway + WAF
For single-region web apps without Front Door:
- Application Gateway v2 with WAF enabled
- OWASP 3.2 rule set + custom rules
- Rate limiting per client IP
- Bot protection (managed rule set)
- SSL termination with Key Vault certificate
---
Monitoring and Alerting
Monitoring Stack
Application Insights (APM + distributed tracing)
│
▼
Log Analytics Workspace (central log store)
│
▼
Azure Monitor Alerts (metric + log-based)
│
▼
Action Groups (email, Teams, PagerDuty, webhook)Essential Alerts
| Alert | Condition | Severity |
|---|---|---|
| App Service HTTP 5xx | > 10 in 5 minutes | Critical (Sev 1) |
| App Service response time | P95 > 2 seconds | Warning (Sev 2) |
| Azure SQL DTU/CPU | > 80% for 10 minutes | Warning (Sev 2) |
| Azure SQL deadlocks | > 0 | Warning (Sev 2) |
| Cosmos DB throttled requests | 429 count > 10 in 5 min | Warning (Sev 2) |
| AKS node CPU | > 80% for 10 minutes | Warning (Sev 2) |
| AKS pod restart count | > 5 in 10 minutes | Critical (Sev 1) |
| Key Vault access denied | > 0 | Critical (Sev 1) |
| Budget threshold | 80% of monthly budget | Warning (Sev 3) |
| Budget threshold | 100% of monthly budget | Critical (Sev 1) |
KQL Queries for Troubleshooting
App Service slow requests:
requests
| where duration > 2000
| summarize count(), avg(duration), percentile(duration, 95) by name
| order by count_ desc
| take 10Failed dependencies (SQL, HTTP, etc.):
dependencies
| where success == false
| summarize count() by type, target, resultCode
| order by count_ descAKS pod errors:
KubePodInventory
| where PodStatus != "Running" and PodStatus != "Succeeded"
| summarize count() by PodStatus, Namespace, Name
| order by count_ descApplication Insights Configuration
- Enable distributed tracing with W3C trace context
- Set sampling to 5-10% for high-volume production (100% for dev)
- Enable profiler for .NET applications
- Enable snapshot debugger for exception analysis
- Configure availability tests (URL ping every 5 minutes from multiple regions)
---
Disaster Recovery
RPO/RTO Mapping
| Tier | RPO | RTO | Strategy | Cost |
|---|---|---|---|---|
| Tier 1 (critical) | < 5 minutes | < 1 hour | Active-active multi-region | 2x |
| Tier 2 (important) | < 1 hour | < 4 hours | Warm standby | 1.3x |
| Tier 3 (standard) | < 24 hours | < 24 hours | Backup and restore | 1.1x |
| Tier 4 (non-critical) | < 72 hours | < 72 hours | Rebuild from IaC | 1x |
Backup Strategy
| Service | Backup Method | Retention |
|---|---|---|
| Azure SQL | Automated backups | 7 days (short-term), 10 years (long-term) |
| Cosmos DB | Continuous backup + point-in-time restore | 7-30 days |
| Blob Storage | Soft delete + versioning + geo-redundant | 30 days soft delete |
| AKS | Velero backup to Blob Storage | 7 days |
| Key Vault | Soft delete + purge protection | 90 days |
| App Service | Manual or automated (Backup and Restore feature) | Custom |
Storage Redundancy
| Redundancy | Regions | Durability | Use Case |
|---|---|---|---|
| LRS | 1 (3 copies) | 11 nines | Dev/test, easily recreatable data |
| ZRS | 1 (3 AZs) | 12 nines | Production, zone failure protection |
| GRS | 2 (6 copies) | 16 nines | Business-critical, regional failure protection |
| GZRS | 2 (3 AZs + secondary) | 16 nines | Most critical data, best protection |
Default to ZRS for production. Use GRS/GZRS only when cross-region DR is required.
DR Testing Checklist
- [ ] Verify automated backups are running and retention is correct
- [ ] Test point-in-time restore for databases (monthly)
- [ ] Test regional failover for SQL failover groups (quarterly)
- [ ] Validate IaC can recreate full environment from scratch
- [ ] Test Front Door failover by taking down primary region health endpoint
- [ ] Document and test runbook for manual failover steps
- [ ] Measure actual RTO vs target during DR drill
---
Common Pitfalls
Cost Pitfalls
| Pitfall | Impact | Prevention |
|---|---|---|
| No budget alerts | Unexpected bills | Set alerts at 50%, 80%, 100% on day one |
| Premium tier in dev/test | 3-5x overspend | Use Basic/Free tiers, auto-shutdown VMs |
| Orphaned resources | Silent monthly charges | Tag everything, review Cost Management weekly |
| Ignoring Reserved Instances | 35-55% overpay on steady workloads | Review Azure Advisor quarterly |
| Over-provisioned Cosmos DB RU/s | Paying for unused throughput | Use autoscale or serverless |
Security Pitfalls
| Pitfall | Impact | Prevention |
|---|---|---|
| Secrets in App Settings | Leaked credentials | Use Key Vault references |
| Public PaaS endpoints | Exposed attack surface | Private Endpoints + VNet integration |
| Contributor role on subscription | Overprivileged access | Scope to resource group, use PIM |
| No diagnostic settings | Blind to attacks | Enable on every resource from day one |
| SQL password authentication | Weak identity model | Entra-only auth, Managed Identity |
Operational Pitfalls
| Pitfall | Impact | Prevention |
|---|---|---|
| Manual portal deployments | Drift, no audit trail | Bicep for everything, block portal changes via Policy |
| No health checks configured | Silent failures | /health endpoint, Front Door probes, App Service checks |
| Single region deployment | Single point of failure | At minimum, use Availability Zones |
| No tagging strategy | Cannot track costs/ownership | Enforce via Azure Policy from day one |
| Ignoring Azure Advisor | Missed optimizations | Weekly review, enable email digest |
Azure Service Selection Guide
Quick reference for choosing the right Azure service based on workload requirements.
---
Table of Contents
- Compute Services
- Database Services
- Storage Services
- Messaging and Events
- Networking
- Security and Identity
- Monitoring and Observability
---
Compute Services
Decision Matrix
| Requirement | Recommended Service |
|---|---|
| Event-driven, short tasks (<10 min) | Azure Functions (Consumption) |
| Event-driven, longer tasks (<30 min) | Azure Functions (Premium) |
| Containerized apps, simple deployment | Azure Container Apps |
| Full Kubernetes control | AKS |
| Traditional web apps (PaaS) | App Service |
| GPU, HPC, custom OS | Virtual Machines |
| Batch processing | Azure Batch |
| Simple container from source | App Service (container) |
Azure Functions vs Container Apps vs AKS vs App Service
| Feature | Functions | Container Apps | AKS | App Service |
|---|---|---|---|---|
| Scale to zero | Yes (Consumption) | Yes | No (min 1 node) | No |
| Kubernetes | No | Built on K8s (abstracted) | Full K8s | No |
| Cold start | 1-5s (Consumption) | 0-2s | N/A | N/A |
| Max execution time | 10 min (Consumption), 30 min (Premium) | Unlimited | Unlimited | Unlimited |
| Languages | C#, JS, Python, Java, Go, Rust, PowerShell | Any container | Any container | .NET, Node, Python, Java, PHP, Ruby |
| Pricing model | Per-execution | Per vCPU-second | Per node | Per plan |
| Best for | Event handlers, APIs, scheduled jobs | Microservices, APIs | Complex platforms, multi-team | Web apps, APIs, mobile backends |
| Operational complexity | Low | Low-Medium | High | Low |
| Dapr integration | No | Built-in | Manual | No |
| KEDA autoscaling | No | Built-in | Manual install | No |
Opinionated recommendation:
- Start with App Service for web apps and APIs — simplest operational model.
- Use Container Apps for microservices — serverless containers without Kubernetes complexity.
- Use AKS only when you need full Kubernetes API access (custom operators, service mesh, multi-cluster).
- Use Functions for event-driven glue (queue processing, webhooks, scheduled jobs).
VM Size Selection
| Workload | Series | Example | vCPUs | RAM | Use Case |
|---|---|---|---|---|---|
| General purpose | Dv5/Dsv5 | Standard_D4s_v5 | 4 | 16 GB | Web servers, small databases |
| Memory optimized | Ev5/Esv5 | Standard_E8s_v5 | 8 | 64 GB | Databases, caching, analytics |
| Compute optimized | Fv2/Fsv2 | Standard_F8s_v2 | 8 | 16 GB | Batch processing, ML inference |
| Storage optimized | Lsv3 | Standard_L8s_v3 | 8 | 64 GB | Data warehouses, large databases |
| GPU | NCv3/NDv4 | Standard_NC6s_v3 | 6 | 112 GB | ML training, rendering |
Always use v5 generation or newer — better price-performance than older series.
---
Database Services
Decision Matrix
| Requirement | Recommended Service |
|---|---|
| Relational, SQL Server compatible | Azure SQL Database |
| Relational, PostgreSQL | Azure Database for PostgreSQL Flexible Server |
| Relational, MySQL | Azure Database for MySQL Flexible Server |
| Document / multi-model, global distribution | Cosmos DB |
| Key-value cache, sessions | Azure Cache for Redis |
| Time-series, IoT data | Azure Data Explorer (Kusto) |
| Full-text search | Azure AI Search (formerly Cognitive Search) |
| Graph database | Cosmos DB (Gremlin API) |
Cosmos DB vs Azure SQL vs PostgreSQL
| Feature | Cosmos DB | Azure SQL | PostgreSQL Flexible |
|---|---|---|---|
| Data model | Document, key-value, graph, table, column | Relational | Relational + JSON |
| Global distribution | Native multi-region writes | Geo-replication (async) | Read replicas |
| Consistency | 5 levels (strong to eventual) | Strong | Strong |
| Scaling | RU/s (auto or manual) | DTU or vCore | vCore |
| Serverless tier | Yes | Yes | No |
| Best for | Global apps, variable schema, low-latency reads | OLTP, complex queries, transactions | PostgreSQL ecosystem, extensions |
| Pricing model | Per RU/s + storage | Per DTU or per vCore | Per vCore |
| Managed backups | Continuous + point-in-time | Automatic + long-term retention | Automatic |
Opinionated recommendation:
- Default to Azure SQL Serverless for most relational workloads — auto-pause saves money in dev/staging.
- Use PostgreSQL Flexible when you need PostGIS, full-text search, or specific PostgreSQL extensions.
- Use Cosmos DB only when you need global distribution, sub-10ms latency, or flexible schema.
- Never use Cosmos DB for workloads that need complex joins or transactions across partitions.
Azure SQL Tier Selection
| Tier | Use Case | Compute | Cost Range |
|---|---|---|---|
| Basic / S0 | Dev/test, tiny workloads | 5 DTUs | $5/month |
| General Purpose (Serverless) | Variable workloads, dev/staging | 0.5-40 vCores (auto-pause) | $40-800/month |
| General Purpose (Provisioned) | Steady production workloads | 2-80 vCores | $150-3000/month |
| Business Critical | High IOPS, low latency, readable secondary | 2-128 vCores | $400-8000/month |
| Hyperscale | Large databases (>4 TB), instant scaling | 2-128 vCores | $200-5000/month |
---
Storage Services
Decision Matrix
| Requirement | Recommended Service |
|---|---|
| Unstructured data (files, images, backups) | Blob Storage |
| File shares (SMB/NFS) | Azure Files |
| High-performance file shares | Azure NetApp Files |
| Data Lake (analytics, big data) | Data Lake Storage Gen2 |
| Disk storage for VMs | Managed Disks |
| Queue-based messaging (simple) | Queue Storage |
| Table data (simple key-value) | Table Storage (or Cosmos DB Table API) |
Blob Storage Tiers
| Tier | Access Pattern | Cost (per GB/month) | Access Cost | Use Case |
|---|---|---|---|---|
| Hot | Frequent access | $0.018 | Low | Active data, web content |
| Cool | Infrequent (30+ days) | $0.01 | Medium | Backups, older data |
| Cold | Rarely accessed (90+ days) | $0.0036 | Higher | Compliance archives |
| Archive | Almost never (180+ days) | $0.00099 | High (rehydrate required) | Long-term retention |
Always set lifecycle management policies. Rule of thumb: Hot for 30 days, Cool for 90 days, Cold or Archive after that.
---
Messaging and Events
Decision Matrix
| Requirement | Recommended Service |
|---|---|
| Pub/sub, event routing, reactive | Event Grid |
| Reliable message queues, transactions | Service Bus |
| High-throughput event streaming | Event Hubs |
| Simple task queues | Queue Storage |
| IoT device telemetry | IoT Hub |
Event Grid vs Service Bus vs Event Hubs
| Feature | Event Grid | Service Bus | Event Hubs |
|---|---|---|---|
| Pattern | Pub/Sub events | Message queue / topic | Event streaming |
| Delivery | At-least-once | At-least-once (peek-lock) | At-least-once (partitioned) |
| Ordering | No guarantee | FIFO (sessions) | Per partition |
| Max message size | 1 MB | 256 KB (Standard), 100 MB (Premium) | 1 MB (Standard), 20 MB (Premium) |
| Retention | 24 hours | 14 days (Standard) | 1-90 days |
| Throughput | Millions/sec | Thousands/sec | Millions/sec |
| Best for | Reactive events, webhooks | Business workflows, commands | Telemetry, logs, analytics |
| Dead letter | Yes | Yes | Via capture to storage |
Opinionated recommendation:
- Event Grid for reactive, fan-out scenarios (blob uploaded, resource created, custom events).
- Service Bus for reliable business messaging (orders, payments, workflows). Use topics for pub/sub, queues for point-to-point.
- Event Hubs for high-volume telemetry, log aggregation, and streaming analytics.
---
Networking
Decision Matrix
| Requirement | Recommended Service |
|---|---|
| Global HTTP load balancing + CDN + WAF | Azure Front Door |
| Regional Layer 7 load balancing + WAF | Application Gateway |
| Regional Layer 4 load balancing | Azure Load Balancer |
| DNS management | Azure DNS |
| DNS-based global traffic routing | Traffic Manager |
| Private connectivity to PaaS | Private Endpoints |
| Site-to-site VPN | VPN Gateway |
| Dedicated private connection | ExpressRoute |
| Outbound internet from VNet | NAT Gateway |
| DDoS protection | Azure DDoS Protection |
Front Door vs Application Gateway vs Load Balancer
| Feature | Front Door | Application Gateway | Load Balancer |
|---|---|---|---|
| Layer | 7 (HTTP/HTTPS) | 7 (HTTP/HTTPS) | 4 (TCP/UDP) |
| Scope | Global | Regional | Regional |
| WAF | Yes (Premium) | Yes (v2) | No |
| SSL termination | Yes | Yes | No |
| CDN | Built-in | No | No |
| Health probes | Yes | Yes | Yes |
| Best for | Global web apps, multi-region | Single-region web apps | TCP/UDP workloads, internal LB |
---
Security and Identity
Decision Matrix
| Requirement | Recommended Service |
|---|---|
| User authentication | Entra ID (Azure AD) |
| B2C customer identity | Entra External ID (Azure AD B2C) |
| Secrets, keys, certificates | Key Vault |
| Service-to-service auth | Managed Identity |
| Network access control | NSGs + Private Endpoints |
| Web application firewall | Front Door WAF or App Gateway WAF |
| Threat detection | Microsoft Defender for Cloud |
| Policy enforcement | Azure Policy |
| Privileged access management | Entra ID PIM |
Managed Identity Usage
| Scenario | Configuration |
|---|---|
| App Service accessing SQL | System-assigned MI + Azure SQL Entra auth |
| Functions accessing Key Vault | System-assigned MI + Key Vault RBAC |
| AKS pods accessing Cosmos DB | Workload Identity + Cosmos DB RBAC |
| VM accessing Storage | System-assigned MI + Storage RBAC |
| DevOps pipeline deploying | Workload Identity Federation (no secrets) |
Rule: Every Azure service that supports Managed Identity should use it. No connection strings with passwords, no service principal secrets in config.
---
Monitoring and Observability
Decision Matrix
| Requirement | Recommended Service |
|---|---|
| Application performance monitoring | Application Insights |
| Log aggregation and queries | Log Analytics (KQL) |
| Metrics and alerts | Azure Monitor |
| Dashboards | Azure Dashboard or Grafana (managed) |
| Distributed tracing | Application Insights (OpenTelemetry) |
| Cost monitoring | Cost Management + Budgets |
| Security monitoring | Microsoft Defender for Cloud |
| Compliance monitoring | Azure Policy + Regulatory Compliance |
Every resource should have diagnostic settings sending logs and metrics to a Log Analytics workspace. Non-negotiable for production.
#!/usr/bin/env python3
"""
Azure architecture design and service recommendation tool.
Generates architecture patterns based on application requirements.
Usage:
python architecture_designer.py --app-type web_app --users 10000
python architecture_designer.py --app-type microservices --users 50000 --requirements '{"compliance": ["HIPAA"]}'
python architecture_designer.py --app-type serverless --users 5000 --json
"""
import argparse
import json
import sys
from typing import Dict, List, Any
# ---------------------------------------------------------------------------
# Azure service catalog used by the designer
# ---------------------------------------------------------------------------
ARCHITECTURE_PATTERNS = {
"web_app": {
"small": "app_service_web",
"medium": "app_service_scaled",
"large": "multi_region_web",
},
"saas_platform": {
"small": "app_service_web",
"medium": "aks_microservices",
"large": "multi_region_web",
},
"mobile_backend": {
"small": "serverless_functions",
"medium": "app_service_web",
"large": "aks_microservices",
},
"microservices": {
"small": "container_apps",
"medium": "aks_microservices",
"large": "aks_microservices",
},
"data_pipeline": {
"small": "serverless_data",
"medium": "synapse_pipeline",
"large": "synapse_pipeline",
},
"serverless": {
"small": "serverless_functions",
"medium": "serverless_functions",
"large": "serverless_functions",
},
}
def _size_bucket(users: int) -> str:
if users < 10000:
return "small"
if users < 100000:
return "medium"
return "large"
# ---------------------------------------------------------------------------
# Pattern builders
# ---------------------------------------------------------------------------
def _app_service_web(users: int, reqs: Dict) -> Dict[str, Any]:
budget = reqs.get("budget_monthly_usd", 500)
return {
"recommended_pattern": "app_service_web",
"description": "Azure App Service with managed SQL and CDN",
"use_case": "Web apps, SaaS platforms, startup MVPs",
"service_stack": [
"App Service (Linux P1v3)",
"Azure SQL Database (Serverless GP_S_Gen5_2)",
"Azure Front Door",
"Azure Blob Storage",
"Key Vault",
"Entra ID + RBAC",
"Application Insights",
],
"estimated_monthly_cost_usd": min(280, budget),
"cost_breakdown": {
"App Service P1v3": "$70-95",
"Azure SQL Serverless": "$40-120",
"Front Door": "$35-55",
"Blob Storage": "$5-15",
"Key Vault": "$1-5",
"Application Insights": "$5-20",
},
"pros": [
"Managed platform — no OS patching",
"Built-in autoscale and deployment slots",
"Easy CI/CD with GitHub Actions or Azure DevOps",
"Custom domains and TLS certificates included",
"Integrated authentication (Easy Auth)",
],
"cons": [
"Less control than VMs or containers",
"Platform constraints for exotic runtimes",
"Cold start on lower-tier plans",
"Outbound IP shared unless isolated tier",
],
"scaling": {
"users_supported": "1k - 100k",
"requests_per_second": "100 - 10,000",
"method": "App Service autoscale rules (CPU, memory, HTTP queue)",
},
}
def _aks_microservices(users: int, reqs: Dict) -> Dict[str, Any]:
budget = reqs.get("budget_monthly_usd", 2000)
return {
"recommended_pattern": "aks_microservices",
"description": "Microservices on AKS with API Management and Cosmos DB",
"use_case": "Complex SaaS, multi-team microservices, high-scale platforms",
"service_stack": [
"AKS (3 node pools: system, app, jobs)",
"API Management (Standard v2)",
"Cosmos DB (multi-model)",
"Service Bus (Standard)",
"Azure Container Registry",
"Azure Monitor + Application Insights",
"Key Vault",
"Entra ID workload identity",
],
"estimated_monthly_cost_usd": min(1200, budget),
"cost_breakdown": {
"AKS node pools (D4s_v5 x3)": "$350-500",
"API Management Standard v2": "$175",
"Cosmos DB": "$100-400",
"Service Bus Standard": "$10-50",
"Container Registry Basic": "$5",
"Azure Monitor": "$50-100",
"Key Vault": "$1-5",
},
"pros": [
"Full Kubernetes ecosystem",
"Independent scaling per service",
"Multi-language and multi-framework",
"Mature ecosystem (Helm, Keda, Dapr)",
"Workload identity — no credentials in pods",
],
"cons": [
"Kubernetes operational complexity",
"Higher baseline cost",
"Requires dedicated platform team",
"Networking (CNI, ingress) configuration heavy",
],
"scaling": {
"users_supported": "10k - 10M",
"requests_per_second": "1,000 - 1,000,000",
"method": "Cluster autoscaler + KEDA event-driven autoscaling",
},
}
def _container_apps(users: int, reqs: Dict) -> Dict[str, Any]:
budget = reqs.get("budget_monthly_usd", 500)
return {
"recommended_pattern": "container_apps",
"description": "Serverless containers on Azure Container Apps",
"use_case": "Microservices without Kubernetes management overhead",
"service_stack": [
"Azure Container Apps",
"Azure Container Registry",
"Cosmos DB",
"Service Bus",
"Key Vault",
"Application Insights",
"Entra ID managed identity",
],
"estimated_monthly_cost_usd": min(350, budget),
"cost_breakdown": {
"Container Apps (consumption)": "$50-150",
"Container Registry Basic": "$5",
"Cosmos DB": "$50-150",
"Service Bus Standard": "$10-30",
"Key Vault": "$1-5",
"Application Insights": "$5-20",
},
"pros": [
"Serverless containers — scale to zero",
"Built-in Dapr integration",
"KEDA autoscaling included",
"No cluster management",
"Simpler networking than AKS",
],
"cons": [
"Less control than full AKS",
"Limited to HTTP and event-driven workloads",
"Smaller ecosystem than Kubernetes",
"Some advanced features still in preview",
],
"scaling": {
"users_supported": "1k - 500k",
"requests_per_second": "100 - 50,000",
"method": "KEDA scalers (HTTP, queue length, CPU, custom)",
},
}
def _serverless_functions(users: int, reqs: Dict) -> Dict[str, Any]:
budget = reqs.get("budget_monthly_usd", 300)
return {
"recommended_pattern": "serverless_functions",
"description": "Azure Functions with Event Grid and Cosmos DB",
"use_case": "Event-driven backends, APIs, scheduled jobs, webhooks",
"service_stack": [
"Azure Functions (Consumption plan)",
"Event Grid",
"Service Bus",
"Cosmos DB (Serverless)",
"Azure Blob Storage",
"Application Insights",
"Key Vault",
],
"estimated_monthly_cost_usd": min(80, budget),
"cost_breakdown": {
"Functions (Consumption)": "$0-20 (1M free executions/month)",
"Event Grid": "$0-5",
"Service Bus Basic": "$0-10",
"Cosmos DB Serverless": "$5-40",
"Blob Storage": "$2-10",
"Application Insights": "$5-15",
},
"pros": [
"Pay-per-execution — true serverless",
"Scale to zero, scale to millions",
"Multiple trigger types (HTTP, queue, timer, blob, event)",
"Durable Functions for orchestration",
"Fast development cycle",
],
"cons": [
"Cold start latency (1-5s on consumption plan)",
"10-minute execution timeout on consumption plan",
"Limited local development experience",
"Debugging distributed functions is complex",
],
"scaling": {
"users_supported": "1k - 1M",
"requests_per_second": "100 - 100,000",
"method": "Automatic (Azure Functions runtime scales instances)",
},
}
def _synapse_pipeline(users: int, reqs: Dict) -> Dict[str, Any]:
budget = reqs.get("budget_monthly_usd", 1500)
return {
"recommended_pattern": "synapse_pipeline",
"description": "Data pipeline with Event Hubs, Synapse, and Data Lake",
"use_case": "Data warehousing, ETL, analytics, ML pipelines",
"service_stack": [
"Event Hubs (Standard)",
"Data Factory / Synapse Pipelines",
"Data Lake Storage Gen2",
"Synapse Analytics (Serverless SQL pool)",
"Azure Functions (processing)",
"Power BI",
"Azure Monitor",
],
"estimated_monthly_cost_usd": min(800, budget),
"cost_breakdown": {
"Event Hubs Standard": "$20-80",
"Data Factory": "$50-200",
"Data Lake Storage Gen2": "$20-80",
"Synapse Serverless SQL": "$50-300 (per TB scanned)",
"Azure Functions": "$10-40",
"Power BI Pro": "$10/user/month",
},
"pros": [
"Unified analytics platform (Synapse)",
"Serverless SQL — pay per query",
"Native Spark integration",
"Data Lake Gen2 — hierarchical namespace, cheap storage",
"Built-in data integration (90+ connectors)",
],
"cons": [
"Synapse learning curve",
"Cost unpredictable with serverless SQL at scale",
"Complex permissions model (Synapse RBAC + storage ACLs)",
"Spark pool startup time",
],
"scaling": {
"events_per_second": "1,000 - 10,000,000",
"data_volume": "1 GB - 1 PB per day",
"method": "Event Hubs throughput units + Synapse auto-scale",
},
}
def _serverless_data(users: int, reqs: Dict) -> Dict[str, Any]:
budget = reqs.get("budget_monthly_usd", 300)
return {
"recommended_pattern": "serverless_data",
"description": "Lightweight data pipeline with Functions and Data Lake",
"use_case": "Small-scale ETL, event processing, log aggregation",
"service_stack": [
"Azure Functions",
"Event Grid",
"Data Lake Storage Gen2",
"Azure SQL Serverless",
"Application Insights",
],
"estimated_monthly_cost_usd": min(120, budget),
"cost_breakdown": {
"Azure Functions": "$0-20",
"Event Grid": "$0-5",
"Data Lake Storage Gen2": "$5-20",
"Azure SQL Serverless": "$20-60",
"Application Insights": "$5-15",
},
"pros": [
"Very low cost for small volumes",
"Serverless end-to-end",
"Simple to operate",
"Scales automatically",
],
"cons": [
"Not suitable for high-volume analytics",
"Limited transformation capabilities",
"No built-in orchestration (use Durable Functions)",
],
"scaling": {
"events_per_second": "10 - 10,000",
"data_volume": "1 MB - 100 GB per day",
"method": "Azure Functions auto-scale",
},
}
def _multi_region_web(users: int, reqs: Dict) -> Dict[str, Any]:
budget = reqs.get("budget_monthly_usd", 5000)
return {
"recommended_pattern": "multi_region_web",
"description": "Multi-region active-active deployment with Front Door",
"use_case": "Global applications, 99.99% uptime, data residency compliance",
"service_stack": [
"Azure Front Door (Premium)",
"App Service (2+ regions) or AKS (2+ regions)",
"Cosmos DB (multi-region writes)",
"Azure SQL (geo-replication or failover groups)",
"Traffic Manager (DNS failover)",
"Azure Monitor + Log Analytics (centralized)",
"Key Vault (per region)",
],
"estimated_monthly_cost_usd": min(3000, budget),
"cost_breakdown": {
"Front Door Premium": "$100-200",
"Compute (2 regions)": "$300-1000",
"Cosmos DB (multi-region)": "$400-1500",
"Azure SQL geo-replication": "$200-600",
"Monitoring": "$50-150",
"Data transfer (cross-region)": "$50-200",
},
"pros": [
"Global low latency",
"99.99% availability",
"Automatic failover",
"Data residency compliance",
"Front Door WAF at the edge",
],
"cons": [
"1.5-2x cost vs single region",
"Data consistency challenges (Cosmos DB conflict resolution)",
"Complex deployment pipeline",
"Cross-region data transfer costs",
],
"scaling": {
"users_supported": "100k - 100M",
"requests_per_second": "10,000 - 10,000,000",
"method": "Per-region autoscale + Front Door global routing",
},
}
PATTERN_DISPATCH = {
"app_service_web": _app_service_web,
"app_service_scaled": _app_service_web, # same builder, cost adjusts
"aks_microservices": _aks_microservices,
"container_apps": _container_apps,
"serverless_functions": _serverless_functions,
"synapse_pipeline": _synapse_pipeline,
"serverless_data": _serverless_data,
"multi_region_web": _multi_region_web,
}
# ---------------------------------------------------------------------------
# Core recommendation logic
# ---------------------------------------------------------------------------
def recommend(app_type: str, users: int, requirements: Dict) -> Dict[str, Any]:
"""Return architecture recommendation for the given inputs."""
bucket = _size_bucket(users)
patterns = ARCHITECTURE_PATTERNS.get(app_type, ARCHITECTURE_PATTERNS["web_app"])
pattern_key = patterns.get(bucket, "app_service_web")
builder = PATTERN_DISPATCH.get(pattern_key, _app_service_web)
result = builder(users, requirements)
# Add compliance notes if relevant
compliance = requirements.get("compliance", [])
if compliance:
result["compliance_notes"] = []
if "HIPAA" in compliance:
result["compliance_notes"].append(
"Enable Microsoft Defender for Cloud, BAA agreement, audit logging, encryption at rest with CMK"
)
if "SOC2" in compliance:
result["compliance_notes"].append(
"Azure Policy SOC 2 initiative, Defender for Cloud regulatory compliance dashboard"
)
if "GDPR" in compliance:
result["compliance_notes"].append(
"Data residency in EU region, Purview for data classification, consent management"
)
if "ISO27001" in compliance or "ISO 27001" in compliance:
result["compliance_notes"].append(
"Azure Policy ISO 27001 initiative, audit logs to Log Analytics, access reviews in Entra ID"
)
return result
def generate_checklist(result: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Return an implementation checklist for the recommended architecture."""
services = result.get("service_stack", [])
return [
{
"phase": "Planning",
"tasks": [
"Review architecture pattern and Azure services",
"Estimate costs with Azure Pricing Calculator",
"Define environment strategy (dev, staging, production)",
"Set up Azure subscription and resource groups",
"Define tagging strategy (environment, owner, cost-center, app-name)",
],
},
{
"phase": "Foundation",
"tasks": [
"Create VNet with subnets (app, data, management)",
"Configure NSGs and Private Endpoints",
"Set up Entra ID groups and RBAC assignments",
"Create Key Vault and seed with initial secrets",
"Enable Microsoft Defender for Cloud",
],
},
{
"phase": "Core Services",
"tasks": [f"Deploy {svc}" for svc in services],
},
{
"phase": "Security",
"tasks": [
"Enable Managed Identity on all services",
"Configure Private Endpoints for PaaS resources",
"Set up Application Gateway or Front Door with WAF",
"Assign Azure Policy initiatives (CIS, SOC 2, etc.)",
"Enable diagnostic settings on all resources",
],
},
{
"phase": "Monitoring",
"tasks": [
"Create Log Analytics workspace",
"Enable Application Insights for all services",
"Create Azure Monitor alert rules for critical metrics",
"Set up Action Groups for notifications (email, Teams, PagerDuty)",
"Create Azure Dashboard for operational visibility",
],
},
{
"phase": "CI/CD",
"tasks": [
"Set up Azure DevOps or GitHub Actions pipeline",
"Configure workload identity federation (no secrets in CI)",
"Implement Bicep deployment pipeline with what-if preview",
"Set up staging slots or blue-green deployment",
"Document rollback procedures",
],
},
]
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def _format_text(result: Dict[str, Any]) -> str:
lines = []
lines.append(f"Pattern: {result['recommended_pattern']}")
lines.append(f"Description: {result['description']}")
lines.append(f"Use Case: {result['use_case']}")
lines.append(f"Estimated Monthly Cost: ${result['estimated_monthly_cost_usd']}")
lines.append("")
lines.append("Service Stack:")
for svc in result.get("service_stack", []):
lines.append(f" - {svc}")
lines.append("")
lines.append("Cost Breakdown:")
for k, v in result.get("cost_breakdown", {}).items():
lines.append(f" {k}: {v}")
lines.append("")
lines.append("Pros:")
for p in result.get("pros", []):
lines.append(f" + {p}")
lines.append("")
lines.append("Cons:")
for c in result.get("cons", []):
lines.append(f" - {c}")
if result.get("compliance_notes"):
lines.append("")
lines.append("Compliance Notes:")
for note in result["compliance_notes"]:
lines.append(f" * {note}")
lines.append("")
lines.append("Scaling:")
for k, v in result.get("scaling", {}).items():
lines.append(f" {k}: {v}")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(
description="Azure Architecture Designer — recommend Azure architecture patterns based on application requirements.",
epilog="Examples:\n"
" python architecture_designer.py --app-type web_app --users 10000\n"
" python architecture_designer.py --app-type microservices --users 50000 --json\n"
' python architecture_designer.py --app-type serverless --users 5000 --requirements \'{"compliance":["HIPAA"]}\'',
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"--app-type",
required=True,
choices=["web_app", "saas_platform", "mobile_backend", "microservices", "data_pipeline", "serverless"],
help="Application type to design for",
)
parser.add_argument(
"--users",
type=int,
default=1000,
help="Expected number of users (default: 1000)",
)
parser.add_argument(
"--requirements",
type=str,
default="{}",
help="JSON string of additional requirements (budget_monthly_usd, compliance, etc.)",
)
parser.add_argument(
"--checklist",
action="store_true",
help="Include implementation checklist in output",
)
parser.add_argument(
"--json",
action="store_true",
dest="json_output",
help="Output as JSON instead of human-readable text",
)
args = parser.parse_args()
try:
reqs = json.loads(args.requirements)
except json.JSONDecodeError as exc:
print(f"Error: invalid --requirements JSON: {exc}", file=sys.stderr)
sys.exit(1)
result = recommend(args.app_type, args.users, reqs)
if args.checklist:
result["implementation_checklist"] = generate_checklist(result)
if args.json_output:
print(json.dumps(result, indent=2))
else:
print(_format_text(result))
if args.checklist:
print("\n--- Implementation Checklist ---")
for phase in result["implementation_checklist"]:
print(f"\n{phase['phase']}:")
for task in phase["tasks"]:
print(f" [ ] {task}")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Azure Bicep template generator.
Generates Bicep infrastructure-as-code scaffolds for common Azure architecture patterns.
Usage:
python bicep_generator.py --arch-type web-app
python bicep_generator.py --arch-type microservices --output main.bicep
python bicep_generator.py --arch-type serverless --json
python bicep_generator.py --help
"""
import argparse
import json
import sys
from typing import Dict
# ---------------------------------------------------------------------------
# Bicep templates
# ---------------------------------------------------------------------------
def _web_app_template() -> str:
return r"""// =============================================================================
// Azure Web App Architecture — Bicep Template
// App Service + Azure SQL + Front Door + Key Vault + Application Insights
// =============================================================================
@description('Environment name')
@allowed(['dev', 'staging', 'production'])
param environment string = 'dev'
@description('Azure region')
param location string = resourceGroup().location
@description('Application name (lowercase, no spaces)')
@minLength(3)
@maxLength(20)
param appName string
@description('SQL admin Entra ID object ID')
param sqlAdminObjectId string
// ---------------------------------------------------------------------------
// Key Vault
// ---------------------------------------------------------------------------
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: '${environment}-${appName}-kv'
location: location
properties: {
sku: { family: 'A', name: 'standard' }
tenantId: subscription().tenantId
enableRbacAuthorization: true
enableSoftDelete: true
softDeleteRetentionInDays: 30
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
}
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// App Service Plan + App Service
// ---------------------------------------------------------------------------
resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = {
name: '${environment}-${appName}-plan'
location: location
sku: {
name: environment == 'production' ? 'P1v3' : 'B1'
tier: environment == 'production' ? 'PremiumV3' : 'Basic'
capacity: 1
}
properties: {
reserved: true // Linux
}
tags: {
environment: environment
'app-name': appName
}
}
resource appService 'Microsoft.Web/sites@2023-01-01' = {
name: '${environment}-${appName}-web'
location: location
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true
siteConfig: {
linuxFxVersion: 'NODE|20-lts'
minTlsVersion: '1.2'
ftpsState: 'Disabled'
alwaysOn: environment == 'production'
healthCheckPath: '/health'
}
}
identity: {
type: 'SystemAssigned'
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Azure SQL (Serverless)
// ---------------------------------------------------------------------------
resource sqlServer 'Microsoft.Sql/servers@2023-05-01-preview' = {
name: '${environment}-${appName}-sql'
location: location
properties: {
administrators: {
administratorType: 'ActiveDirectory'
azureADOnlyAuthentication: true
principalType: 'Group'
sid: sqlAdminObjectId
tenantId: subscription().tenantId
}
minimalTlsVersion: '1.2'
publicNetworkAccess: environment == 'production' ? 'Disabled' : 'Enabled'
}
tags: {
environment: environment
'app-name': appName
}
}
resource sqlDatabase 'Microsoft.Sql/servers/databases@2023-05-01-preview' = {
parent: sqlServer
name: '${appName}-db'
location: location
sku: {
name: 'GP_S_Gen5_2'
tier: 'GeneralPurpose'
}
properties: {
autoPauseDelay: environment == 'production' ? -1 : 60
minCapacity: json('0.5')
zoneRedundant: environment == 'production'
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Application Insights + Log Analytics
// ---------------------------------------------------------------------------
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: '${environment}-${appName}-logs'
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: environment == 'production' ? 90 : 30
}
tags: {
environment: environment
'app-name': appName
}
}
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: '${environment}-${appName}-ai'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalytics.id
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Outputs
// ---------------------------------------------------------------------------
output appServiceUrl string = 'https://${appService.properties.defaultHostName}'
output keyVaultUri string = keyVault.properties.vaultUri
output appInsightsKey string = appInsights.properties.InstrumentationKey
output sqlServerFqdn string = sqlServer.properties.fullyQualifiedDomainName
"""
def _microservices_template() -> str:
return r"""// =============================================================================
// Azure Microservices Architecture — Bicep Template
// AKS + API Management + Cosmos DB + Service Bus + Key Vault
// =============================================================================
@description('Environment name')
@allowed(['dev', 'staging', 'production'])
param environment string = 'dev'
@description('Azure region')
param location string = resourceGroup().location
@description('Application name')
@minLength(3)
@maxLength(20)
param appName string
@description('AKS admin Entra ID group object ID')
param aksAdminGroupId string
// ---------------------------------------------------------------------------
// Key Vault
// ---------------------------------------------------------------------------
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: '${environment}-${appName}-kv'
location: location
properties: {
sku: { family: 'A', name: 'standard' }
tenantId: subscription().tenantId
enableRbacAuthorization: true
enableSoftDelete: true
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// AKS Cluster
// ---------------------------------------------------------------------------
resource aksCluster 'Microsoft.ContainerService/managedClusters@2024-01-01' = {
name: '${environment}-${appName}-aks'
location: location
identity: { type: 'SystemAssigned' }
properties: {
dnsPrefix: '${environment}-${appName}'
kubernetesVersion: '1.29'
enableRBAC: true
aadProfile: {
managed: true
adminGroupObjectIDs: [aksAdminGroupId]
enableAzureRBAC: true
}
networkProfile: {
networkPlugin: 'azure'
networkPolicy: 'azure'
serviceCidr: '10.0.0.0/16'
dnsServiceIP: '10.0.0.10'
}
agentPoolProfiles: [
{
name: 'system'
count: environment == 'production' ? 3 : 1
vmSize: 'Standard_D2s_v5'
mode: 'System'
enableAutoScaling: true
minCount: 1
maxCount: 3
availabilityZones: environment == 'production' ? ['1', '2', '3'] : []
}
{
name: 'app'
count: environment == 'production' ? 3 : 1
vmSize: 'Standard_D4s_v5'
mode: 'User'
enableAutoScaling: true
minCount: 1
maxCount: 10
availabilityZones: environment == 'production' ? ['1', '2', '3'] : []
}
]
addonProfiles: {
omsagent: {
enabled: true
config: {
logAnalyticsWorkspaceResourceID: logAnalytics.id
}
}
}
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Container Registry
// ---------------------------------------------------------------------------
resource acr 'Microsoft.ContainerRegistry/registries@2023-07-01' = {
name: '${environment}${appName}acr'
location: location
sku: { name: environment == 'production' ? 'Standard' : 'Basic' }
properties: {
adminUserEnabled: false
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Cosmos DB (Serverless for dev, Autoscale for prod)
// ---------------------------------------------------------------------------
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
name: '${environment}-${appName}-cosmos'
location: location
kind: 'GlobalDocumentDB'
properties: {
databaseAccountOfferType: 'Standard'
consistencyPolicy: { defaultConsistencyLevel: 'Session' }
locations: [
{ locationName: location, failoverPriority: 0, isZoneRedundant: environment == 'production' }
]
capabilities: environment == 'dev' ? [{ name: 'EnableServerless' }] : []
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Service Bus
// ---------------------------------------------------------------------------
resource serviceBus 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' = {
name: '${environment}-${appName}-sb'
location: location
sku: { name: 'Standard', tier: 'Standard' }
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Log Analytics + Application Insights
// ---------------------------------------------------------------------------
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: '${environment}-${appName}-logs'
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: environment == 'production' ? 90 : 30
}
tags: {
environment: environment
'app-name': appName
}
}
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: '${environment}-${appName}-ai'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalytics.id
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Outputs
// ---------------------------------------------------------------------------
output aksClusterName string = aksCluster.name
output acrLoginServer string = acr.properties.loginServer
output cosmosEndpoint string = cosmosAccount.properties.documentEndpoint
output serviceBusEndpoint string = '${serviceBus.name}.servicebus.windows.net'
output keyVaultUri string = keyVault.properties.vaultUri
"""
def _serverless_template() -> str:
return r"""// =============================================================================
// Azure Serverless Architecture — Bicep Template
// Azure Functions + Event Grid + Service Bus + Cosmos DB
// =============================================================================
@description('Environment name')
@allowed(['dev', 'staging', 'production'])
param environment string = 'dev'
@description('Azure region')
param location string = resourceGroup().location
@description('Application name')
@minLength(3)
@maxLength(20)
param appName string
// ---------------------------------------------------------------------------
// Storage Account (required by Functions)
// ---------------------------------------------------------------------------
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: '${environment}${appName}st'
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
properties: {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Azure Functions (Consumption Plan)
// ---------------------------------------------------------------------------
resource functionPlan 'Microsoft.Web/serverfarms@2023-01-01' = {
name: '${environment}-${appName}-func-plan'
location: location
sku: {
name: 'Y1'
tier: 'Dynamic'
}
properties: {
reserved: true // Linux
}
tags: {
environment: environment
'app-name': appName
}
}
resource functionApp 'Microsoft.Web/sites@2023-01-01' = {
name: '${environment}-${appName}-func'
location: location
kind: 'functionapp,linux'
identity: { type: 'SystemAssigned' }
properties: {
serverFarmId: functionPlan.id
httpsOnly: true
siteConfig: {
linuxFxVersion: 'NODE|20'
minTlsVersion: '1.2'
ftpsState: 'Disabled'
appSettings: [
{ name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=core.windows.net;AccountKey=${storageAccount.listKeys().keys[0].value}' }
{ name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
{ name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' }
{ name: 'APPINSIGHTS_INSTRUMENTATIONKEY', value: appInsights.properties.InstrumentationKey }
{ name: 'COSMOS_ENDPOINT', value: cosmosAccount.properties.documentEndpoint }
{ name: 'SERVICE_BUS_CONNECTION', value: listKeys('${serviceBus.id}/AuthorizationRules/RootManageSharedAccessKey', serviceBus.apiVersion).primaryConnectionString }
]
}
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Cosmos DB (Serverless)
// ---------------------------------------------------------------------------
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
name: '${environment}-${appName}-cosmos'
location: location
kind: 'GlobalDocumentDB'
properties: {
databaseAccountOfferType: 'Standard'
consistencyPolicy: { defaultConsistencyLevel: 'Session' }
locations: [
{ locationName: location, failoverPriority: 0 }
]
capabilities: [{ name: 'EnableServerless' }]
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Service Bus
// ---------------------------------------------------------------------------
resource serviceBus 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' = {
name: '${environment}-${appName}-sb'
location: location
sku: { name: 'Basic', tier: 'Basic' }
tags: {
environment: environment
'app-name': appName
}
}
resource orderQueue 'Microsoft.ServiceBus/namespaces/queues@2022-10-01-preview' = {
parent: serviceBus
name: 'orders'
properties: {
maxDeliveryCount: 5
defaultMessageTimeToLive: 'P7D'
deadLetteringOnMessageExpiration: true
lockDuration: 'PT1M'
}
}
// ---------------------------------------------------------------------------
// Application Insights + Log Analytics
// ---------------------------------------------------------------------------
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: '${environment}-${appName}-logs'
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: 30
}
tags: {
environment: environment
'app-name': appName
}
}
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: '${environment}-${appName}-ai'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalytics.id
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Outputs
// ---------------------------------------------------------------------------
output functionAppUrl string = 'https://${functionApp.properties.defaultHostName}'
output cosmosEndpoint string = cosmosAccount.properties.documentEndpoint
output serviceBusEndpoint string = '${serviceBus.name}.servicebus.windows.net'
output appInsightsKey string = appInsights.properties.InstrumentationKey
"""
def _data_pipeline_template() -> str:
return r"""// =============================================================================
// Azure Data Pipeline Architecture — Bicep Template
// Event Hubs + Data Lake Gen2 + Synapse Analytics + Azure Functions
// =============================================================================
@description('Environment name')
@allowed(['dev', 'staging', 'production'])
param environment string = 'dev'
@description('Azure region')
param location string = resourceGroup().location
@description('Application name')
@minLength(3)
@maxLength(20)
param appName string
// ---------------------------------------------------------------------------
// Data Lake Storage Gen2
// ---------------------------------------------------------------------------
resource dataLake 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: '${environment}${appName}dl'
location: location
sku: { name: environment == 'production' ? 'Standard_ZRS' : 'Standard_LRS' }
kind: 'StorageV2'
properties: {
isHnsEnabled: true // Hierarchical namespace for Data Lake Gen2
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
}
tags: {
environment: environment
'app-name': appName
}
}
resource rawContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
name: '${dataLake.name}/default/raw'
properties: { publicAccess: 'None' }
}
resource curatedContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
name: '${dataLake.name}/default/curated'
properties: { publicAccess: 'None' }
}
// ---------------------------------------------------------------------------
// Event Hubs
// ---------------------------------------------------------------------------
resource eventHubNamespace 'Microsoft.EventHub/namespaces@2023-01-01-preview' = {
name: '${environment}-${appName}-eh'
location: location
sku: {
name: 'Standard'
tier: 'Standard'
capacity: environment == 'production' ? 2 : 1
}
properties: {
minimumTlsVersion: '1.2'
}
tags: {
environment: environment
'app-name': appName
}
}
resource eventHub 'Microsoft.EventHub/namespaces/eventhubs@2023-01-01-preview' = {
parent: eventHubNamespace
name: 'ingest'
properties: {
partitionCount: environment == 'production' ? 8 : 2
messageRetentionInDays: 7
}
}
resource consumerGroup 'Microsoft.EventHub/namespaces/eventhubs/consumergroups@2023-01-01-preview' = {
parent: eventHub
name: 'processing'
}
// ---------------------------------------------------------------------------
// Synapse Analytics (Serverless SQL)
// ---------------------------------------------------------------------------
resource synapse 'Microsoft.Synapse/workspaces@2021-06-01' = {
name: '${environment}-${appName}-syn'
location: location
identity: { type: 'SystemAssigned' }
properties: {
defaultDataLakeStorage: {
accountUrl: 'https://${dataLake.name}.dfs.core.windows.net'
filesystem: 'curated'
}
sqlAdministratorLogin: 'sqladmin'
sqlAdministratorLoginPassword: 'REPLACE_WITH_KEYVAULT_REFERENCE'
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Log Analytics
// ---------------------------------------------------------------------------
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: '${environment}-${appName}-logs'
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: 30
}
tags: {
environment: environment
'app-name': appName
}
}
// ---------------------------------------------------------------------------
// Outputs
// ---------------------------------------------------------------------------
output dataLakeEndpoint string = 'https://${dataLake.name}.dfs.core.windows.net'
output eventHubNamespace string = eventHubNamespace.name
output synapseEndpoint string = synapse.properties.connectivityEndpoints.sql
"""
TEMPLATES: Dict[str, callable] = {
"web-app": _web_app_template,
"microservices": _microservices_template,
"serverless": _serverless_template,
"data-pipeline": _data_pipeline_template,
}
TEMPLATE_DESCRIPTIONS = {
"web-app": "App Service + Azure SQL + Front Door + Key Vault + Application Insights",
"microservices": "AKS + API Management + Cosmos DB + Service Bus + Key Vault",
"serverless": "Azure Functions + Event Grid + Service Bus + Cosmos DB",
"data-pipeline": "Event Hubs + Data Lake Gen2 + Synapse Analytics + Azure Functions",
}
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(
description="Azure Bicep Generator — generate Bicep IaC templates for common Azure architecture patterns.",
epilog="Examples:\n"
" python bicep_generator.py --arch-type web-app\n"
" python bicep_generator.py --arch-type microservices --output main.bicep\n"
" python bicep_generator.py --arch-type serverless --json",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"--arch-type",
required=True,
choices=list(TEMPLATES.keys()),
help="Architecture pattern type",
)
parser.add_argument(
"--output",
type=str,
default=None,
help="Write Bicep to file instead of stdout",
)
parser.add_argument(
"--json",
action="store_true",
dest="json_output",
help="Output metadata as JSON (template content + description)",
)
args = parser.parse_args()
template_fn = TEMPLATES[args.arch_type]
bicep_content = template_fn()
if args.json_output:
result = {
"arch_type": args.arch_type,
"description": TEMPLATE_DESCRIPTIONS[args.arch_type],
"bicep_template": bicep_content,
"lines": len(bicep_content.strip().split("\n")),
}
print(json.dumps(result, indent=2))
elif args.output:
with open(args.output, "w") as f:
f.write(bicep_content)
print(f"Bicep template written to {args.output} ({len(bicep_content.strip().split(chr(10)))} lines)")
print(f"Pattern: {TEMPLATE_DESCRIPTIONS[args.arch_type]}")
print(f"\nNext steps:")
print(f" 1. az bicep build --file {args.output}")
print(f" 2. az deployment group validate --resource-group <rg> --template-file {args.output}")
print(f" 3. az deployment group create --resource-group <rg> --template-file {args.output}")
else:
print(bicep_content)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Azure cost optimization analyzer.
Analyzes Azure resource configurations and provides cost-saving recommendations.
Usage:
python cost_optimizer.py --config resources.json
python cost_optimizer.py --config resources.json --json
python cost_optimizer.py --help
Expected JSON config format:
{
"virtual_machines": [
{"name": "vm-web-01", "size": "Standard_D4s_v5", "cpu_utilization": 12, "pricing": "on-demand", "monthly_cost": 140}
],
"sql_databases": [
{"name": "sqldb-main", "tier": "GeneralPurpose", "vcores": 8, "utilization": 25, "monthly_cost": 400}
],
"storage_accounts": [
{"name": "stmyapp", "size_gb": 500, "tier": "Hot", "has_lifecycle_policy": false}
],
"aks_clusters": [
{"name": "aks-prod", "node_count": 6, "node_size": "Standard_D4s_v5", "avg_cpu_utilization": 35, "monthly_cost": 800}
],
"cosmos_db": [
{"name": "cosmos-orders", "ru_provisioned": 10000, "ru_used_avg": 2000, "monthly_cost": 580}
],
"public_ips": [
{"name": "pip-unused", "attached": false}
],
"app_services": [
{"name": "app-web", "tier": "PremiumV3", "instance_count": 3, "cpu_utilization": 15, "monthly_cost": 300}
],
"has_budget_alerts": false,
"has_advisor_enabled": false
}
"""
import argparse
import json
import sys
from typing import Dict, List, Any
class AzureCostOptimizer:
"""Analyze Azure resource configurations and recommend cost savings."""
def __init__(self, resources: Dict[str, Any]):
self.resources = resources
self.recommendations: List[Dict[str, Any]] = []
def analyze(self) -> Dict[str, Any]:
"""Run all analysis passes and return full report."""
self.recommendations = []
total_savings = 0.0
total_savings += self._analyze_virtual_machines()
total_savings += self._analyze_sql_databases()
total_savings += self._analyze_storage()
total_savings += self._analyze_aks()
total_savings += self._analyze_cosmos_db()
total_savings += self._analyze_app_services()
total_savings += self._analyze_networking()
total_savings += self._analyze_general()
current_spend = self._estimate_current_spend()
return {
"current_monthly_usd": round(current_spend, 2),
"potential_monthly_savings_usd": round(total_savings, 2),
"optimized_monthly_usd": round(current_spend - total_savings, 2),
"savings_percentage": round((total_savings / current_spend) * 100, 2) if current_spend > 0 else 0,
"recommendations": self.recommendations,
"priority_actions": self._top_priority(),
}
# ------------------------------------------------------------------
# Analysis passes
# ------------------------------------------------------------------
def _analyze_virtual_machines(self) -> float:
savings = 0.0
vms = self.resources.get("virtual_machines", [])
for vm in vms:
cost = vm.get("monthly_cost", 140)
cpu = vm.get("cpu_utilization", 100)
pricing = vm.get("pricing", "on-demand")
# Idle VMs
if cpu < 5:
savings += cost * 0.9
self.recommendations.append({
"service": "Virtual Machines",
"type": "Idle Resource",
"issue": f"VM {vm.get('name', '?')} has <5% CPU utilization",
"recommendation": "Deallocate or delete the VM. Use Azure Automation auto-shutdown for dev/test VMs.",
"potential_savings_usd": round(cost * 0.9, 2),
"priority": "high",
})
elif cpu < 20:
savings += cost * 0.4
self.recommendations.append({
"service": "Virtual Machines",
"type": "Right-sizing",
"issue": f"VM {vm.get('name', '?')} is under-utilized ({cpu}% CPU)",
"recommendation": "Downsize to a smaller SKU. Use Azure Advisor right-sizing recommendations.",
"potential_savings_usd": round(cost * 0.4, 2),
"priority": "high",
})
# Reserved Instances
if pricing == "on-demand" and cpu >= 20:
ri_savings = cost * 0.35
savings += ri_savings
self.recommendations.append({
"service": "Virtual Machines",
"type": "Reserved Instances",
"issue": f"VM {vm.get('name', '?')} runs on-demand with steady utilization",
"recommendation": "Purchase 1-year Reserved Instance (up to 35% savings) or 3-year (up to 55% savings).",
"potential_savings_usd": round(ri_savings, 2),
"priority": "medium",
})
# Spot VMs for batch/fault-tolerant workloads
spot_candidates = [vm for vm in vms if vm.get("workload_type") in ("batch", "dev", "test")]
if spot_candidates:
spot_savings = sum(vm.get("monthly_cost", 100) * 0.6 for vm in spot_candidates)
savings += spot_savings
self.recommendations.append({
"service": "Virtual Machines",
"type": "Spot VMs",
"issue": f"{len(spot_candidates)} VMs running batch/dev/test workloads on regular instances",
"recommendation": "Switch to Azure Spot VMs for up to 90% savings on interruptible workloads.",
"potential_savings_usd": round(spot_savings, 2),
"priority": "medium",
})
return savings
def _analyze_sql_databases(self) -> float:
savings = 0.0
dbs = self.resources.get("sql_databases", [])
for db in dbs:
cost = db.get("monthly_cost", 200)
utilization = db.get("utilization", 100)
vcores = db.get("vcores", 2)
tier = db.get("tier", "GeneralPurpose")
# Idle databases
if db.get("connections_per_day", 1000) < 10:
savings += cost * 0.8
self.recommendations.append({
"service": "Azure SQL",
"type": "Idle Resource",
"issue": f"Database {db.get('name', '?')} has <10 connections/day",
"recommendation": "Delete unused database or switch to serverless tier with auto-pause.",
"potential_savings_usd": round(cost * 0.8, 2),
"priority": "high",
})
# Serverless opportunity
elif utilization < 30 and tier == "GeneralPurpose":
serverless_savings = cost * 0.45
savings += serverless_savings
self.recommendations.append({
"service": "Azure SQL",
"type": "Serverless Migration",
"issue": f"Database {db.get('name', '?')} has low utilization ({utilization}%) on provisioned tier",
"recommendation": "Switch to Azure SQL Serverless tier with auto-pause (60-min delay). Pay only for active compute.",
"potential_savings_usd": round(serverless_savings, 2),
"priority": "high",
})
# Right-sizing
elif utilization < 50 and vcores > 2:
right_size_savings = cost * 0.3
savings += right_size_savings
self.recommendations.append({
"service": "Azure SQL",
"type": "Right-sizing",
"issue": f"Database {db.get('name', '?')} uses {vcores} vCores at {utilization}% utilization",
"recommendation": f"Reduce to {max(2, vcores // 2)} vCores. Monitor DTU/vCore usage after change.",
"potential_savings_usd": round(right_size_savings, 2),
"priority": "medium",
})
return savings
def _analyze_storage(self) -> float:
savings = 0.0
accounts = self.resources.get("storage_accounts", [])
for acct in accounts:
size_gb = acct.get("size_gb", 0)
tier = acct.get("tier", "Hot")
# Lifecycle policy missing
if not acct.get("has_lifecycle_policy", False) and size_gb > 50:
lifecycle_savings = size_gb * 0.01 # ~$0.01/GB moving hot to cool
savings += lifecycle_savings
self.recommendations.append({
"service": "Blob Storage",
"type": "Lifecycle Policy",
"issue": f"Account {acct.get('name', '?')} ({size_gb} GB) has no lifecycle policy",
"recommendation": "Add lifecycle management: move to Cool after 30 days, Archive after 90 days.",
"potential_savings_usd": round(lifecycle_savings, 2),
"priority": "medium",
})
# Hot tier for large, infrequently accessed data
if tier == "Hot" and size_gb > 500:
tier_savings = size_gb * 0.008
savings += tier_savings
self.recommendations.append({
"service": "Blob Storage",
"type": "Storage Tier",
"issue": f"Account {acct.get('name', '?')} ({size_gb} GB) on Hot tier",
"recommendation": "Evaluate Cool or Cold tier for infrequently accessed data. Hot=$0.018/GB, Cool=$0.01/GB, Cold=$0.0036/GB.",
"potential_savings_usd": round(tier_savings, 2),
"priority": "high",
})
return savings
def _analyze_aks(self) -> float:
savings = 0.0
clusters = self.resources.get("aks_clusters", [])
for cluster in clusters:
cost = cluster.get("monthly_cost", 500)
cpu = cluster.get("avg_cpu_utilization", 100)
node_count = cluster.get("node_count", 3)
# Over-provisioned cluster
if cpu < 30 and node_count > 3:
aks_savings = cost * 0.3
savings += aks_savings
self.recommendations.append({
"service": "AKS",
"type": "Right-sizing",
"issue": f"Cluster {cluster.get('name', '?')} has {node_count} nodes at {cpu}% CPU",
"recommendation": "Enable cluster autoscaler. Set min nodes to 2 (or 1 for dev). Use node auto-provisioning.",
"potential_savings_usd": round(aks_savings, 2),
"priority": "high",
})
# Spot node pools for non-critical workloads
if not cluster.get("has_spot_pool", False):
spot_savings = cost * 0.15
savings += spot_savings
self.recommendations.append({
"service": "AKS",
"type": "Spot Node Pools",
"issue": f"Cluster {cluster.get('name', '?')} has no spot node pools",
"recommendation": "Add a spot node pool for batch jobs, CI runners, and dev workloads (up to 90% savings).",
"potential_savings_usd": round(spot_savings, 2),
"priority": "medium",
})
return savings
def _analyze_cosmos_db(self) -> float:
savings = 0.0
dbs = self.resources.get("cosmos_db", [])
for db in dbs:
cost = db.get("monthly_cost", 200)
ru_provisioned = db.get("ru_provisioned", 400)
ru_used = db.get("ru_used_avg", 400)
# Massive over-provisioning
if ru_provisioned > 0 and ru_used / ru_provisioned < 0.2:
cosmos_savings = cost * 0.5
savings += cosmos_savings
self.recommendations.append({
"service": "Cosmos DB",
"type": "Right-sizing",
"issue": f"Container {db.get('name', '?')} uses {ru_used}/{ru_provisioned} RU/s ({int(ru_used/ru_provisioned*100)}% utilization)",
"recommendation": "Switch to autoscale throughput or serverless mode. Autoscale adjusts RU/s between 10%-100% of max.",
"potential_savings_usd": round(cosmos_savings, 2),
"priority": "high",
})
elif ru_provisioned > 0 and ru_used / ru_provisioned < 0.5:
cosmos_savings = cost * 0.25
savings += cosmos_savings
self.recommendations.append({
"service": "Cosmos DB",
"type": "Autoscale",
"issue": f"Container {db.get('name', '?')} uses {ru_used}/{ru_provisioned} RU/s — variable workload",
"recommendation": "Enable autoscale throughput. Set max RU/s to current provisioned value.",
"potential_savings_usd": round(cosmos_savings, 2),
"priority": "medium",
})
return savings
def _analyze_app_services(self) -> float:
savings = 0.0
apps = self.resources.get("app_services", [])
for app in apps:
cost = app.get("monthly_cost", 100)
cpu = app.get("cpu_utilization", 100)
instances = app.get("instance_count", 1)
tier = app.get("tier", "Basic")
# Over-provisioned instances
if cpu < 20 and instances > 1:
app_savings = cost * 0.4
savings += app_savings
self.recommendations.append({
"service": "App Service",
"type": "Right-sizing",
"issue": f"App {app.get('name', '?')} runs {instances} instances at {cpu}% CPU",
"recommendation": "Reduce instance count or enable autoscale with min=1. Consider downgrading plan tier.",
"potential_savings_usd": round(app_savings, 2),
"priority": "high",
})
# Premium tier for dev/test
if tier in ("PremiumV3", "PremiumV2") and app.get("environment") in ("dev", "test"):
tier_savings = cost * 0.5
savings += tier_savings
self.recommendations.append({
"service": "App Service",
"type": "Plan Tier",
"issue": f"App {app.get('name', '?')} uses {tier} in {app.get('environment', 'unknown')} environment",
"recommendation": "Use Basic (B1) or Free tier for dev/test environments.",
"potential_savings_usd": round(tier_savings, 2),
"priority": "high",
})
return savings
def _analyze_networking(self) -> float:
savings = 0.0
# Unattached public IPs
pips = self.resources.get("public_ips", [])
unattached = [p for p in pips if not p.get("attached", True)]
if unattached:
pip_savings = len(unattached) * 3.65 # ~$0.005/hr = $3.65/month
savings += pip_savings
self.recommendations.append({
"service": "Public IP",
"type": "Unused Resource",
"issue": f"{len(unattached)} unattached public IPs incurring hourly charges",
"recommendation": "Delete unused public IPs. Unattached Standard SKU IPs cost ~$3.65/month each.",
"potential_savings_usd": round(pip_savings, 2),
"priority": "high",
})
# NAT Gateway in dev environments
nat_gateways = self.resources.get("nat_gateways", [])
dev_nats = [n for n in nat_gateways if n.get("environment") in ("dev", "test")]
if dev_nats:
nat_savings = len(dev_nats) * 32 # ~$32/month per NAT Gateway
savings += nat_savings
self.recommendations.append({
"service": "NAT Gateway",
"type": "Environment Optimization",
"issue": f"{len(dev_nats)} NAT Gateways in dev/test environments",
"recommendation": "Remove NAT Gateways in dev/test. Use Azure Firewall or service tags for outbound instead.",
"potential_savings_usd": round(nat_savings, 2),
"priority": "medium",
})
return savings
def _analyze_general(self) -> float:
savings = 0.0
if not self.resources.get("has_budget_alerts", False):
self.recommendations.append({
"service": "Cost Management",
"type": "Budget Alerts",
"issue": "No budget alerts configured",
"recommendation": "Create Azure Budget with alerts at 50%, 80%, and 100% of monthly target.",
"potential_savings_usd": 0,
"priority": "high",
})
if not self.resources.get("has_advisor_enabled", True):
self.recommendations.append({
"service": "Azure Advisor",
"type": "Visibility",
"issue": "Azure Advisor cost recommendations not reviewed",
"recommendation": "Review Azure Advisor cost recommendations weekly. Enable Advisor alerts for new findings.",
"potential_savings_usd": 0,
"priority": "medium",
})
return savings
# ------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------
def _estimate_current_spend(self) -> float:
total = 0.0
for key in ("virtual_machines", "sql_databases", "aks_clusters", "cosmos_db", "app_services"):
for item in self.resources.get(key, []):
total += item.get("monthly_cost", 0)
# Storage estimate
for acct in self.resources.get("storage_accounts", []):
total += acct.get("size_gb", 0) * 0.018 # Hot tier default
# Public IPs
for pip in self.resources.get("public_ips", []):
total += 3.65
return total if total > 0 else 1000 # Default if no cost data
def _top_priority(self) -> List[Dict[str, Any]]:
high = [r for r in self.recommendations if r["priority"] == "high"]
high.sort(key=lambda x: x.get("potential_savings_usd", 0), reverse=True)
return high[:5]
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def _format_text(report: Dict[str, Any]) -> str:
lines = []
lines.append(f"Current Monthly Spend: ${report['current_monthly_usd']}")
lines.append(f"Potential Savings: ${report['potential_monthly_savings_usd']} ({report['savings_percentage']}%)")
lines.append(f"Optimized Spend: ${report['optimized_monthly_usd']}")
lines.append("")
lines.append("=== Priority Actions ===")
for i, action in enumerate(report.get("priority_actions", []), 1):
lines.append(f" {i}. [{action['service']}] {action['recommendation']}")
lines.append(f" Savings: ${action.get('potential_savings_usd', 0)}")
lines.append("")
lines.append("=== All Recommendations ===")
for rec in report.get("recommendations", []):
lines.append(f" [{rec['priority'].upper()}] {rec['service']} — {rec['type']}")
lines.append(f" Issue: {rec['issue']}")
lines.append(f" Action: {rec['recommendation']}")
savings = rec.get("potential_savings_usd", 0)
if savings:
lines.append(f" Savings: ${savings}")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(
description="Azure Cost Optimizer — analyze Azure resources and recommend cost savings.",
epilog="Examples:\n"
" python cost_optimizer.py --config resources.json\n"
" python cost_optimizer.py --config resources.json --json",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"--config",
required=True,
help="Path to JSON file with current Azure resource inventory",
)
parser.add_argument(
"--json",
action="store_true",
dest="json_output",
help="Output as JSON instead of human-readable text",
)
args = parser.parse_args()
try:
with open(args.config, "r") as f:
resources = json.load(f)
except FileNotFoundError:
print(f"Error: file not found: {args.config}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as exc:
print(f"Error: invalid JSON in {args.config}: {exc}", file=sys.stderr)
sys.exit(1)
optimizer = AzureCostOptimizer(resources)
report = optimizer.analyze()
if args.json_output:
print(json.dumps(report, indent=2))
else:
print(_format_text(report))
if __name__ == "__main__":
main()
Related skills
FAQ
Which Azure services does azure-cloud-architect cover?
azure-cloud-architect covers AKS, App Service, Azure Functions, Cosmos DB, Bicep and ARM templates, Azure DevOps pipelines, and cost optimization for startup and enterprise workloads.
What does azure-cloud-architect deliver?
azure-cloud-architect delivers cost-bounded Azure architecture recommendations with Bicep infrastructure-as-code templates and DevOps deployment paths based on application type, scale, and compliance requirements.
Is Azure Cloud Architect safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.