Files
James Bland befb8fbaeb feat: initial Claude Code configuration scaffold
Comprehensive Claude Code guidance system with:

- 5 agents: tdd-guardian, code-reviewer, security-scanner, refactor-scan, dependency-audit
- 18 skills covering languages (Python, TypeScript, Rust, Go, Java, C#),
  infrastructure (AWS, Azure, GCP, Terraform, Ansible, Docker/K8s, Database, CI/CD),
  testing (TDD, UI, Browser), and patterns (Monorepo, API Design, Observability)
- 3 hooks: secret detection, auto-formatting, TDD git pre-commit
- Strict TDD enforcement with 80%+ coverage requirements
- Multi-model strategy: Opus for planning, Sonnet for execution (opusplan)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 15:47:34 -05:00

443 lines
14 KiB
Markdown

---
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