--- name: azure-services description: Azure service patterns, RBAC best practices, and common architectures. Use when designing or implementing Azure infrastructure. --- # Azure Services Skill ## Common Architecture Patterns ### Web Application (App Service + Azure SQL) ``` ┌─────────────────────────────────────────────────────────────┐ │ VNet │ │ ┌─────────────────────────────────────────────────────────┐│ │ │ Public Subnet ││ │ │ ┌─────────────┐ ┌─────────────┐ ││ │ │ │ App Gateway │ │ NAT GW │ ││ │ │ └──────┬──────┘ └──────┬──────┘ ││ │ └─────────┼───────────────────────────────────┼───────────┘│ │ │ │ │ │ ┌─────────┼───────────────────────────────────┼───────────┐│ │ │ │ Private Subnet │ ││ │ │ ┌──────▼──────┐ ┌───────▼─────┐ ││ │ │ │ App Service │ │ Azure SQL │ ││ │ │ │ (Web App) │───────────────────▶│ Database │ ││ │ │ └─────────────┘ └─────────────┘ ││ │ └─────────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────────┘ ``` ### Serverless (Azure Functions + API Management) ``` ┌────────────┐ ┌─────────────┐ ┌─────────────┐ │ Front │────▶│ APIM │────▶│ Functions │ │ Door │ │ │ └──────┬──────┘ └────────────┘ └─────────────┘ │ ┌──────────────────────────┼──────────────┐ │ │ │ ┌──────▼─────┐ ┌─────────┐ ┌────▼────┐ │ Cosmos DB │ │ Blob │ │ Key │ └────────────┘ │ Storage │ │ Vault │ └─────────┘ └─────────┘ ``` ## RBAC Best Practices ### Custom Role Definition ```json { "Name": "App Data Reader", "Description": "Read access to application data in storage", "Actions": [ "Microsoft.Storage/storageAccounts/blobServices/containers/read", "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" ], "NotActions": [], "DataActions": [ "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" ], "NotDataActions": [], "AssignableScopes": [ "/subscriptions/{subscription-id}/resourceGroups/{resource-group}" ] } ``` ### Managed Identity Usage ```python # Python - azure-identity from azure.identity import DefaultAzureCredential from azure.keyvault.secrets import SecretClient from azure.storage.blob import BlobServiceClient # Uses managed identity when deployed to Azure credential = DefaultAzureCredential() # Key Vault access secret_client = SecretClient( vault_url="https://my-vault.vault.azure.net/", credential=credential ) secret = secret_client.get_secret("database-password") # Blob Storage access blob_service = BlobServiceClient( account_url="https://mystorageaccount.blob.core.windows.net/", credential=credential ) ``` ```typescript // TypeScript - @azure/identity import { DefaultAzureCredential } from "@azure/identity"; import { SecretClient } from "@azure/keyvault-secrets"; import { BlobServiceClient } from "@azure/storage-blob"; const credential = new DefaultAzureCredential(); // Key Vault access const secretClient = new SecretClient( "https://my-vault.vault.azure.net/", credential ); const secret = await secretClient.getSecret("database-password"); // Blob Storage access const blobService = new BlobServiceClient( "https://mystorageaccount.blob.core.windows.net/", credential ); ``` ## Key Vault Patterns ### Secrets Management ```python from azure.identity import DefaultAzureCredential from azure.keyvault.secrets import SecretClient def get_secret(vault_url: str, secret_name: str) -> str: """Retrieve secret from Key Vault using managed identity.""" credential = DefaultAzureCredential() client = SecretClient(vault_url=vault_url, credential=credential) return client.get_secret(secret_name).value # Usage db_password = get_secret( "https://my-vault.vault.azure.net/", "database-password" ) ``` ### App Service with Key Vault References ```json // App Service configuration { "name": "DatabasePassword", "value": "@Microsoft.KeyVault(SecretUri=https://my-vault.vault.azure.net/secrets/db-password/)", "slotSetting": false } ``` ## Blob Storage Patterns ### SAS Token Generation ```python from datetime import datetime, timedelta from azure.storage.blob import ( BlobServiceClient, generate_blob_sas, BlobSasPermissions, ) def generate_read_sas( account_name: str, account_key: str, container: str, blob_name: str, expiry_hours: int = 1 ) -> str: """Generate a read-only SAS URL for a blob.""" sas_token = generate_blob_sas( account_name=account_name, container_name=container, blob_name=blob_name, account_key=account_key, permission=BlobSasPermissions(read=True), expiry=datetime.utcnow() + timedelta(hours=expiry_hours), ) return f"https://{account_name}.blob.core.windows.net/{container}/{blob_name}?{sas_token}" ``` ### User Delegation SAS (More Secure) ```python from azure.identity import DefaultAzureCredential from azure.storage.blob import BlobServiceClient, UserDelegationKey def generate_user_delegation_sas( account_url: str, container: str, blob_name: str, ) -> str: """Generate SAS using user delegation key (no storage key needed).""" credential = DefaultAzureCredential() blob_service = BlobServiceClient(account_url, credential=credential) # Get user delegation key delegation_key = blob_service.get_user_delegation_key( key_start_time=datetime.utcnow(), key_expiry_time=datetime.utcnow() + timedelta(hours=1) ) sas_token = generate_blob_sas( account_name=blob_service.account_name, container_name=container, blob_name=blob_name, user_delegation_key=delegation_key, permission=BlobSasPermissions(read=True), expiry=datetime.utcnow() + timedelta(hours=1), ) return f"{account_url}/{container}/{blob_name}?{sas_token}" ``` ## Cosmos DB Patterns ### Async Client Usage ```python from azure.cosmos.aio import CosmosClient from azure.identity.aio import DefaultAzureCredential async def get_cosmos_client() -> CosmosClient: """Create async Cosmos client with managed identity.""" credential = DefaultAzureCredential() return CosmosClient( url="https://my-cosmos.documents.azure.com:443/", credential=credential ) async def query_items(container_name: str, query: str) -> list: """Query items from Cosmos DB container.""" async with await get_cosmos_client() as client: database = client.get_database_client("my-database") container = database.get_container_client(container_name) items = [] async for item in container.query_items( query=query, enable_cross_partition_query=True ): items.append(item) return items ``` ### Partition Key Design ```python # Good partition key choices: # - tenant_id for multi-tenant apps # - user_id for user-specific data # - category for catalog data # Document structure { "id": "order-12345", "partitionKey": "customer-789", # Use customer ID for orders "orderDate": "2024-01-15", "items": [...], "total": 150.00 } ``` ## Azure Functions Patterns ### HTTP Trigger with Input Validation ```python import azure.functions as func import logging from pydantic import BaseModel, ValidationError class CreateOrderRequest(BaseModel): customer_id: str items: list[dict] app = func.FunctionApp() @app.route(route="orders", methods=["POST"]) async def create_order(req: func.HttpRequest) -> func.HttpResponse: """Create a new order with validation.""" try: body = req.get_json() request = CreateOrderRequest(**body) # Process order... result = await process_order(request) return func.HttpResponse( body=result.model_dump_json(), status_code=201, mimetype="application/json" ) except ValidationError as e: return func.HttpResponse( body=e.json(), status_code=400, mimetype="application/json" ) except Exception as e: logging.exception("Error processing order") return func.HttpResponse( body='{"error": "Internal server error"}', status_code=500, mimetype="application/json" ) ``` ### Durable Functions Orchestration ```python import azure.functions as func import azure.durable_functions as df app = func.FunctionApp() @app.orchestration_trigger(context_name="context") def order_orchestrator(context: df.DurableOrchestrationContext): """Orchestrate multi-step order processing.""" order = context.get_input() # Step 1: Validate inventory inventory_result = yield context.call_activity( "validate_inventory", order["items"] ) if not inventory_result["available"]: return {"status": "failed", "reason": "insufficient_inventory"} # Step 2: Process payment payment_result = yield context.call_activity( "process_payment", order["payment"] ) if not payment_result["success"]: return {"status": "failed", "reason": "payment_failed"} # Step 3: Create shipment shipment = yield context.call_activity( "create_shipment", order ) return {"status": "completed", "shipment_id": shipment["id"]} ``` ## Application Insights ### Structured Logging ```python import logging from opencensus.ext.azure.log_exporter import AzureLogHandler # Configure logging with Application Insights logger = logging.getLogger(__name__) logger.addHandler(AzureLogHandler( connection_string="InstrumentationKey=xxx;IngestionEndpoint=xxx" )) # Log with custom dimensions logger.info( "Order processed", extra={ "custom_dimensions": { "order_id": "12345", "customer_id": "cust-789", "total": 150.00 } } ) ``` ### Custom Metrics ```python from opencensus.ext.azure import metrics_exporter from opencensus.stats import aggregation, measure, stats, view # Create measure orders_measure = measure.MeasureInt( "orders_processed", "Number of orders processed", "orders" ) # Create view orders_view = view.View( "orders_processed_total", "Total orders processed", [], orders_measure, aggregation.CountAggregation() ) # Register and export view_manager = stats.stats.view_manager view_manager.register_view(orders_view) exporter = metrics_exporter.new_metrics_exporter( connection_string="InstrumentationKey=xxx" ) view_manager.register_exporter(exporter) # Record metric mmap = stats.stats.stats_recorder.new_measurement_map() mmap.measure_int_put(orders_measure, 1) mmap.record() ``` ## CLI Commands ```bash # Authentication az login az account set --subscription "My Subscription" az account show # Resource Groups az group list --output table az group create --name my-rg --location uksouth # Key Vault az keyvault secret show --vault-name my-vault --name my-secret az keyvault secret set --vault-name my-vault --name my-secret --value "secret-value" # Storage az storage blob list --account-name mystorageaccount --container-name mycontainer az storage blob upload --account-name mystorageaccount --container-name mycontainer --file local.txt --name remote.txt # App Service az webapp list --output table az webapp restart --name my-app --resource-group my-rg az webapp log tail --name my-app --resource-group my-rg # Functions az functionapp list --output table az functionapp restart --name my-func --resource-group my-rg # Cosmos DB az cosmosdb list --output table az cosmosdb sql database list --account-name my-cosmos --resource-group my-rg ``` ## Security Checklist - [ ] Use Managed Identities instead of connection strings - [ ] Store secrets in Key Vault, not app settings - [ ] Enable Azure Defender for all resources - [ ] Use Private Endpoints for PaaS services - [ ] Enable diagnostic logging to Log Analytics - [ ] Configure Network Security Groups - [ ] Use User Delegation SAS instead of account keys - [ ] Enable soft delete on Key Vault and Storage - [ ] Configure Azure Policy for compliance - [ ] Enable Microsoft Defender for Cloud