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>
This commit is contained in:
442
.claude/skills/infrastructure/azure/SKILL.md
Normal file
442
.claude/skills/infrastructure/azure/SKILL.md
Normal file
@@ -0,0 +1,442 @@
|
||||
---
|
||||
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
|
||||
Reference in New Issue
Block a user