diff --git a/plugin/skills/azure-application-gateway/SKILL.md b/plugin/skills/azure-application-gateway/SKILL.md new file mode 100644 index 000000000..13f3df7e1 --- /dev/null +++ b/plugin/skills/azure-application-gateway/SKILL.md @@ -0,0 +1,185 @@ +--- +name: azure-application-gateway +description: "Create, configure, and troubleshoot Azure Application Gateway v2 for Layer 7 HTTP/HTTPS load balancing, URL-based routing, SSL/TLS termination, cookie-based affinity, header rewrites, and redirects. Includes WAF v2 integration for web application protection. WHEN: application gateway, app gateway, L7 load balancer, URL routing, path-based routing, SSL offload, SSL termination, web traffic load balancer, cookie affinity, redirect, rewrite headers, autoscale gateway, HTTP load balancing, multi-site hosting. DO NOT USE FOR: L4 TCP/UDP balancing (use azure-load-balancer), global edge routing or CDN (use azure-front-door), standalone WAF policy authoring (use azure-waf)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Application Gateway + +## When to Use This Skill + +- User asks about creating or configuring an Azure Application Gateway +- User needs Layer 7 (HTTP/HTTPS) load balancing within a region +- User wants URL-based or path-based routing (e.g., `/images/*` → one backend, `/api/*` → another) +- User needs SSL/TLS termination or end-to-end SSL encryption +- User asks about multi-site hosting (multiple domains on one gateway) +- User wants cookie-based session affinity +- User needs HTTP-to-HTTPS redirect configuration +- User asks about header rewrite rules or URL rewrite +- User wants to enable WAF (Web Application Firewall) on the gateway +- User needs autoscaling for the application gateway +- User asks about mutual TLS (mTLS) or client certificate authentication +- User needs to troubleshoot 502 Bad Gateway or backend health issues + +## Rules + +1. **Always recommend v2 SKU** — v1 is legacy. Application Gateway v2 supports autoscaling, zone redundancy, static VIP, Key Vault certificate integration, and improved performance. +2. **Dedicated subnet required** — Application Gateway must be deployed in its own subnet (named conventionally `AppGwSubnet`). No other resources allowed in this subnet except other App Gateways. +3. **Minimum subnet size** — Recommend /24 for production. Minimum is /26 for v2 (includes instances + private frontend IP + internal overhead). +4. **Frontend IP** — v2 supports both public and private frontend IPs simultaneously. Static public IP (Standard SKU) is required. +5. **Backend pool targets** — Can include VMs, VMSS, App Services, IP addresses, or FQDNs. Backends can be in different VNets (with peering) or external. +6. **Health probes are critical** — Always configure custom health probes with appropriate paths. Default probe (`/`) may not reflect real application health. +7. **WAF integration** — WAF v2 is a SKU tier on Application Gateway (WAF_v2), not a separate resource. It adds a WAF policy with OWASP rule sets. +8. **SSL certificates** — Recommend Key Vault integration for certificate management. Self-managed PFX upload also supported. +9. **For global routing, use Front Door** — Application Gateway is regional. For multi-region global HTTP routing, CDN, or edge WAF, redirect to azure-front-door. +10. **For L4 balancing, use Load Balancer** — If the user needs TCP/UDP (non-HTTP) balancing, redirect to azure-load-balancer. + +## MCP Tools + +| Tool | Command | Purpose | +|------|---------|---------| +| `azure__network` | `application_gateway_list` | List all application gateways in a subscription/resource group | +| `azure__network` | `application_gateway_get` | Get detailed configuration of a specific application gateway | + +## CLI Fallback + +When MCP tools are unavailable, use these Azure CLI commands: + +```bash +# List application gateways +az network application-gateway list --resource-group -o table + +# Show application gateway details +az network application-gateway show --name -g + +# Create Application Gateway v2 (basic) +az network application-gateway create \ + --name myAppGw \ + --resource-group myRG \ + --sku Standard_v2 \ + --capacity 2 \ + --vnet-name myVNet \ + --subnet AppGwSubnet \ + --public-ip-address myAppGwPIP \ + --frontend-port 80 \ + --http-settings-port 80 \ + --http-settings-protocol Http \ + --routing-rule-type Basic + +# Create with WAF v2 +az network application-gateway create \ + --name myWafAppGw \ + --resource-group myRG \ + --sku WAF_v2 \ + --capacity 2 \ + --vnet-name myVNet \ + --subnet AppGwSubnet \ + --public-ip-address myWafPIP + +# Configure autoscaling +az network application-gateway update \ + --name myAppGw -g myRG \ + --set autoscaleConfiguration.minCapacity=2 \ + --set autoscaleConfiguration.maxCapacity=10 \ + --set sku.capacity=null + +# Add a backend pool +az network application-gateway address-pool create \ + --gateway-name myAppGw -g myRG \ + --name apiBackendPool \ + --servers 10.0.1.4 10.0.1.5 + +# Add an HTTP setting +az network application-gateway http-settings create \ + --gateway-name myAppGw -g myRG \ + --name apiHttpSettings \ + --port 443 \ + --protocol Https \ + --cookie-based-affinity Enabled \ + --timeout 30 + +# Add a health probe +az network application-gateway probe create \ + --gateway-name myAppGw -g myRG \ + --name apiProbe \ + --protocol Https \ + --host-name-from-http-settings true \ + --path "/health" \ + --interval 30 \ + --threshold 3 \ + --timeout 30 + +# Add a URL path map (path-based routing) +az network application-gateway url-path-map create \ + --gateway-name myAppGw -g myRG \ + --name myPathMap \ + --default-address-pool defaultPool \ + --default-http-settings defaultSettings \ + --paths "/api/*" \ + --address-pool apiBackendPool \ + --http-settings apiHttpSettings \ + --rule-name apiPathRule + +# Add a redirect configuration +az network application-gateway redirect-config create \ + --gateway-name myAppGw -g myRG \ + --name httpToHttpsRedirect \ + --type Permanent \ + --target-listener httpsListener \ + --include-path true \ + --include-query-string true + +# Add a rewrite rule set +az network application-gateway rewrite-rule set create \ + --gateway-name myAppGw -g myRG \ + --name myRewriteRuleSet + +# Show backend health +az network application-gateway show-backend-health \ + --name myAppGw -g myRG +``` + +## Key Concepts + +### Application Gateway Components + +| Component | Purpose | Key Config | +|-----------|---------|------------| +| Frontend IP | Client-facing IP (public and/or private) | Static Standard public IP for v2 | +| Listener | Receives incoming requests on port/protocol | HTTP/HTTPS, multi-site (hostname) | +| Rule | Routes listener traffic to backend | Basic (direct) or Path-based | +| Backend Pool | Target servers/services | VMs, VMSS, App Service, IPs, FQDNs | +| HTTP Settings | Backend connection config | Port, protocol, cookie affinity, timeout | +| Health Probe | Backend health monitoring | Protocol, path, interval, threshold | +| URL Path Map | Path-based routing rules | Paths → backend pool + HTTP settings | +| Rewrite Rule | Modify headers/URL | Request/response headers, URL components | +| Redirect Config | HTTP redirect | Permanent/temporary, to listener or URL | +| WAF Policy | Web application firewall | OWASP rules, custom rules, exclusions | +| SSL Certificate | TLS termination | PFX upload or Key Vault reference | + +### SKU Comparison + +| Feature | Standard_v2 | WAF_v2 | +|---------|-------------|--------| +| L7 Load Balancing | ✅ | ✅ | +| Autoscaling | ✅ | ✅ | +| Zone Redundancy | ✅ | ✅ | +| Static VIP | ✅ | ✅ | +| Private Frontend | ✅ | ✅ | +| URL Routing | ✅ | ✅ | +| SSL Termination | ✅ | ✅ | +| Header Rewrites | ✅ | ✅ | +| WAF (OWASP rules) | ❌ | ✅ | +| Bot Protection | ❌ | ✅ | +| Custom WAF Rules | ❌ | ✅ | + +## References + +- [Application Gateway components explained](references/appgw-components.md) +- [URL and path-based routing](references/url-routing.md) +- [SSL/TLS configuration](references/ssl-tls.md) +- [Autoscaling configuration](references/autoscale.md) +- [WAF v2 integration](references/waf-integration.md) diff --git a/plugin/skills/azure-application-gateway/references/appgw-components.md b/plugin/skills/azure-application-gateway/references/appgw-components.md new file mode 100644 index 000000000..ca2e43a6a --- /dev/null +++ b/plugin/skills/azure-application-gateway/references/appgw-components.md @@ -0,0 +1,245 @@ +# Application Gateway Components — How They Connect + +## Component Relationship Flow + +``` +Client Request + │ + ▼ +Frontend IP (public/private) + │ + ▼ +Listener (port + protocol + hostname) + │ + ▼ +Request Routing Rule + ├── Basic Rule → Backend Pool + HTTP Settings + └── Path-based Rule → URL Path Map + ├── /api/* → API Pool + API Settings + ├── /images/* → Static Pool + Static Settings + └── default → Default Pool + Default Settings + │ + ▼ (optionally through) +Rewrite Rules (modify headers/URL before sending to backend) + │ + ▼ +Health Probe verifies backend → Backend Pool member +``` + +## Frontend IP Configurations + +### Public Frontend + +Required for internet-facing applications. + +```bash +# Create Standard public IP (required for v2) +az network public-ip create \ + --name appgw-pip \ + -g myRG \ + --sku Standard \ + --allocation-method Static + +# Associate during gateway creation +az network application-gateway create \ + --name myAppGw -g myRG \ + --sku Standard_v2 \ + --public-ip-address appgw-pip \ + --vnet-name myVNet \ + --subnet AppGwSubnet +``` + +### Private Frontend (v2 Only) + +For internal-only applications (no internet exposure). + +```bash +# Add private frontend IP +az network application-gateway frontend-ip create \ + --gateway-name myAppGw -g myRG \ + --name privateFrontEnd \ + --vnet-name myVNet \ + --subnet AppGwSubnet \ + --private-ip-address 10.0.0.10 +``` + +**Note**: v2 supports both public AND private frontends simultaneously. Listeners bind to a specific frontend IP. + +## Listeners + +Listeners accept incoming connections on a combination of frontend IP, port, protocol, and (optionally) hostname. + +### Basic HTTP Listener + +```bash +az network application-gateway http-listener create \ + --gateway-name myAppGw -g myRG \ + --name httpListener \ + --frontend-ip appGatewayFrontendIP \ + --frontend-port 80 +``` + +### Multi-Site HTTPS Listener + +```bash +# Add frontend port for HTTPS +az network application-gateway frontend-port create \ + --gateway-name myAppGw -g myRG \ + --name port443 --port 443 + +# Create listener with hostname and SSL cert +az network application-gateway http-listener create \ + --gateway-name myAppGw -g myRG \ + --name contoso-https \ + --frontend-ip appGatewayFrontendIP \ + --frontend-port port443 \ + --ssl-cert contoso-cert \ + --host-name "www.contoso.com" +``` + +### Multi-Site Hosting + +Multiple listeners can share the same frontend IP and port by specifying different hostnames: + +| Listener | Frontend IP | Port | Hostname | SSL Cert | +|----------|------------|------|----------|----------| +| contoso-https | Public | 443 | www.contoso.com | contoso-cert | +| fabrikam-https | Public | 443 | www.fabrikam.com | fabrikam-cert | +| wildcard-https | Public | 443 | *.contoso.com | wildcard-cert | + +## Backend Pools + +Backend pools define the targets that serve requests. + +### Supported Backend Types + +| Type | Example | Notes | +|------|---------|-------| +| VM NIC | `10.0.1.4` | IP address of VM | +| VMSS | Instance IPs | Requires IP-based pool | +| App Service | `myapp.azurewebsites.net` | FQDN; requires custom probe with hostname | +| External | `api.partner.com` | Any reachable FQDN or IP | +| Private Endpoint | Private IP | Via VNet integration | + +```bash +# Create pool with IP addresses +az network application-gateway address-pool create \ + --gateway-name myAppGw -g myRG \ + --name webPool \ + --servers 10.0.1.4 10.0.1.5 10.0.1.6 + +# Create pool with FQDN (App Service) +az network application-gateway address-pool create \ + --gateway-name myAppGw -g myRG \ + --name appServicePool \ + --servers myapp.azurewebsites.net +``` + +## HTTP Settings + +HTTP settings define how Application Gateway communicates with backends. + +| Setting | Purpose | Common Values | +|---------|---------|---------------| +| Port | Backend port | 80, 443, 8080 | +| Protocol | HTTP or HTTPS | Use HTTPS for end-to-end encryption | +| Cookie affinity | Session stickiness | Enabled/Disabled | +| Connection draining | Graceful removal | Enabled, 30-3600 sec | +| Request timeout | Backend response timeout | 1-86400 sec (default 20) | +| Override hostname | Rewrite Host header | Required for App Service backends | +| Custom probe | Associated health probe | Always configure for production | +| Trusted root cert | Backend SSL verification | Required for end-to-end HTTPS with self-signed certs | + +```bash +# HTTPS backend settings with App Service hostname override +az network application-gateway http-settings create \ + --gateway-name myAppGw -g myRG \ + --name appServiceSettings \ + --port 443 \ + --protocol Https \ + --cookie-based-affinity Disabled \ + --timeout 30 \ + --host-name-from-backend-pool true +``` + +## Health Probes + +### Default Probe + +If no custom probe is configured, Application Gateway sends probes to `http://127.0.0.1:/` using the backend HTTP settings. This rarely works for real applications. + +### Custom Probe + +```bash +az network application-gateway probe create \ + --gateway-name myAppGw -g myRG \ + --name customProbe \ + --protocol Https \ + --host-name-from-http-settings true \ + --path "/health" \ + --interval 30 \ + --threshold 3 \ + --timeout 30 \ + --match-status-codes "200-399" +``` + +### Probe Parameters + +| Parameter | Description | Recommendation | +|-----------|-------------|----------------| +| `path` | URL path to probe | Use `/health` or `/healthz` | +| `interval` | Seconds between probes | 30 for most workloads | +| `threshold` | Failed probes before marking unhealthy | 3 | +| `timeout` | Seconds to wait for response | Match or exceed app response time | +| `match-status-codes` | HTTP codes considered healthy | `200-399` for most apps | +| `host-name-from-http-settings` | Use Host header from HTTP settings | `true` for App Service backends | + +## Request Routing Rules + +### Basic Rule + +Direct mapping: Listener → one backend pool + HTTP settings. + +```bash +az network application-gateway rule create \ + --gateway-name myAppGw -g myRG \ + --name basicRule \ + --rule-type Basic \ + --http-listener httpListener \ + --address-pool webPool \ + --http-settings defaultSettings \ + --priority 100 +``` + +### Path-Based Rule + +Routes to different backends based on URL path. + +```bash +az network application-gateway rule create \ + --gateway-name myAppGw -g myRG \ + --name pathRule \ + --rule-type PathBasedRouting \ + --http-listener httpListener \ + --url-path-map myPathMap \ + --priority 200 +``` + +**Note (v2)**: All rules require a `--priority` value. Lower numbers = higher priority. + +## Troubleshooting Component Issues + +| Symptom | Component to Check | Action | +|---------|-------------------|--------| +| 502 Bad Gateway | Backend health | `az network application-gateway show-backend-health` | +| 404 Not Found | URL path map / routing rule | Verify path patterns match request URLs | +| SSL errors | Listener certificate | Check cert validity, chain, and Key Vault access | +| Slow response | HTTP settings timeout | Increase timeout; check backend performance | +| Wrong backend | Routing rule priority | Lower priority number wins; check rule ordering | +| Session not sticky | HTTP settings | Enable cookie-based affinity | + +## Source Documentation + +- [Application Gateway components](https://learn.microsoft.com/azure/application-gateway/application-gateway-components) +- [Application Gateway configuration overview](https://learn.microsoft.com/azure/application-gateway/configuration-overview) +- [Troubleshoot backend health](https://learn.microsoft.com/azure/application-gateway/application-gateway-backend-health-troubleshooting) diff --git a/plugin/skills/azure-application-gateway/references/autoscale.md b/plugin/skills/azure-application-gateway/references/autoscale.md new file mode 100644 index 000000000..e9078ddd0 --- /dev/null +++ b/plugin/skills/azure-application-gateway/references/autoscale.md @@ -0,0 +1,169 @@ +# Application Gateway v2 Autoscaling + +## Overview + +Application Gateway v2 supports autoscaling based on traffic load patterns. The gateway automatically adjusts the number of instances up or down based on traffic demand. + +## Scaling Modes + +| Mode | Config | Behavior | +|------|--------|----------| +| Autoscaling | `minCapacity` + `maxCapacity` | Scales between min and max based on load | +| Fixed | `capacity` (no autoscale) | Static number of instances (manual scaling) | + +## Autoscale Configuration + +### Enable Autoscaling + +```bash +# Set autoscale with min 2, max 10 instances +az network application-gateway update \ + --name myAppGw -g myRG \ + --set autoscaleConfiguration.minCapacity=2 \ + --set autoscaleConfiguration.maxCapacity=10 \ + --set sku.capacity=null +``` + +**Important**: Setting `sku.capacity=null` switches from fixed to autoscale mode. + +### Switch to Fixed Capacity + +```bash +az network application-gateway update \ + --name myAppGw -g myRG \ + --capacity 4 \ + --remove autoscaleConfiguration +``` + +### During Initial Deployment + +```bash +az network application-gateway create \ + --name myAppGw -g myRG \ + --sku Standard_v2 \ + --min-capacity 2 \ + --max-capacity 10 \ + --vnet-name myVNet \ + --subnet AppGwSubnet \ + --public-ip-address appgw-pip +``` + +## Capacity Planning + +### Instance Capacity + +Each Application Gateway instance can handle approximately: + +| Metric | Approximate per Instance | +|--------|-------------------------| +| Throughput | ~500 Mbps | +| Connections | ~2,500 concurrent | +| Requests/sec | Varies by request size | +| SSL TPS (RSA 2048) | ~2,500 new connections/sec | +| SSL TPS (ECC 256) | ~8,000 new connections/sec | + +### Sizing Guidelines + +| Workload | Min Capacity | Max Capacity | +|----------|-------------|-------------| +| Dev/Test | 0-1 | 2-3 | +| Small production | 2 | 5-10 | +| Medium production | 2-3 | 10-20 | +| Large / high-traffic | 5-10 | 20-125 | + +**Important considerations:** +- `minCapacity` = 0 means the gateway can scale to zero (saves cost but has cold-start latency ~6-8 minutes) +- `minCapacity` ≥ 2 recommended for production (always-ready, zone-redundant) +- Maximum capacity is **125 instances** per Application Gateway +- Scale-up time is approximately 6-8 minutes per scaling event + +### Minimum Capacity Recommendations + +| Scenario | Min Capacity | Reason | +|----------|-------------|--------| +| Production (always ready) | 2 | Avoids cold start; zone-redundant | +| Dev/test (cost saving) | 0 | Scales to zero when idle | +| High-traffic baseline | Match typical minimum load | Prevents scaling delays during normal operation | +| Spiky traffic | Match normal load | Pre-warms; autoscale handles spikes | + +## Cost Implications + +Application Gateway v2 billing: + +| Component | Charge | +|-----------|--------| +| Fixed cost | Per gateway per hour (even with 0 instances, there's a base cost) | +| Capacity units | Per capacity unit per hour | + +Each **capacity unit** consists of: +- 2,500 persistent connections +- 2.22 Mbps throughput +- 1 compute unit (request processing) + +You pay for whichever is highest. More instances = more capacity units = higher cost. + +### Cost Optimization Tips + +1. **Right-size minCapacity** — Don't over-provision minimum. Use metrics to find baseline. +2. **Set maxCapacity** — Always set a maximum to prevent unexpected costs from traffic spikes or DDoS. +3. **Monitor Capacity Units** — Azure Monitor metric `CapacityUnits` shows actual usage vs. provisioned. +4. **Consider WAF cost** — WAF_v2 has higher per-instance cost than Standard_v2. +5. **Dev/test scale to zero** — Use `minCapacity=0` for non-production. + +## Monitoring Autoscale + +### Key Metrics + +| Metric | Description | Alert Threshold | +|--------|-------------|-----------------| +| `CurrentCapacity` | Current number of instances | Near maxCapacity | +| `CapacityUnits` | Capacity units consumed | > 75% of provisioned | +| `ComputeUnits` | Compute utilization | > 75% per instance | +| `EstimatedBilledCapacityUnits` | Estimated billing units | Budget monitoring | + +```bash +# Check current capacity +az monitor metrics list \ + --resource \ + --metric "CurrentCapacity" \ + --aggregation Average \ + --interval PT5M + +# Check capacity unit utilization +az monitor metrics list \ + --resource \ + --metric "CapacityUnits" \ + --aggregation Average \ + --interval PT5M +``` + +### Autoscale Behavior + +- **Scale up trigger**: When current capacity units exceed 75% threshold +- **Scale down trigger**: When utilization drops below threshold for a sustained period +- **Scale up time**: ~6-8 minutes per instance +- **Scale down time**: Gradual (conservative to avoid flapping) +- **Cooldown**: Built-in to prevent rapid scale in/out cycles + +## Availability Zones + +When `minCapacity` ≥ 2, Application Gateway v2 automatically distributes instances across configured availability zones. + +```bash +# Create zone-redundant App Gateway +az network application-gateway create \ + --name myAppGw -g myRG \ + --sku Standard_v2 \ + --min-capacity 2 \ + --max-capacity 10 \ + --zones 1 2 3 \ + --vnet-name myVNet \ + --subnet AppGwSubnet \ + --public-ip-address appgw-pip +``` + +## Source Documentation + +- [Application Gateway autoscaling](https://learn.microsoft.com/azure/application-gateway/application-gateway-autoscaling-zone-redundant) +- [Application Gateway pricing](https://azure.microsoft.com/pricing/details/application-gateway/) +- [Application Gateway metrics](https://learn.microsoft.com/azure/application-gateway/application-gateway-metrics) diff --git a/plugin/skills/azure-application-gateway/references/ssl-tls.md b/plugin/skills/azure-application-gateway/references/ssl-tls.md new file mode 100644 index 000000000..68e462bbc --- /dev/null +++ b/plugin/skills/azure-application-gateway/references/ssl-tls.md @@ -0,0 +1,183 @@ +# SSL/TLS Configuration + +## SSL/TLS Termination Modes + +| Mode | Description | When to Use | +|------|-------------|-------------| +| SSL Termination | Decrypt at App Gateway, send HTTP to backend | Most common; offloads SSL from backends | +| End-to-End SSL | Decrypt at App Gateway, re-encrypt to backend (HTTPS) | Compliance requires encryption in transit everywhere | +| SSL Passthrough | Not supported | Use Azure Load Balancer or other L4 solution | + +## SSL Termination (Frontend Only) + +Application Gateway decrypts SSL/TLS traffic and forwards plain HTTP to backends. + +```bash +# Upload PFX certificate +az network application-gateway ssl-cert create \ + --gateway-name myAppGw -g myRG \ + --name myCert \ + --cert-file ./cert.pfx \ + --cert-password "MyP@ssword123" + +# Create HTTPS listener with the certificate +az network application-gateway http-listener create \ + --gateway-name myAppGw -g myRG \ + --name httpsListener \ + --frontend-ip appGatewayFrontendIP \ + --frontend-port port443 \ + --ssl-cert myCert + +# Backend HTTP settings (unencrypted to backend) +az network application-gateway http-settings create \ + --gateway-name myAppGw -g myRG \ + --name httpSettings \ + --port 80 \ + --protocol Http +``` + +## End-to-End SSL + +Application Gateway decrypts, inspects, then re-encrypts traffic to HTTPS backends. + +```bash +# Upload backend's root CA certificate (for backend cert verification) +az network application-gateway root-cert create \ + --gateway-name myAppGw -g myRG \ + --name backendRootCA \ + --cert-file ./backend-root-ca.cer + +# HTTPS backend settings with trusted root cert +az network application-gateway http-settings create \ + --gateway-name myAppGw -g myRG \ + --name httpsBackendSettings \ + --port 443 \ + --protocol Https \ + --root-certs backendRootCA \ + --host-name-from-backend-pool true +``` + +### When Trusted Root Cert is Required + +| Backend Cert Type | Root Cert Needed? | +|-------------------|-------------------| +| Public CA (DigiCert, Let's Encrypt, etc.) | No — already trusted | +| Self-signed certificate | Yes — upload the root CA | +| Internal CA / Enterprise CA | Yes — upload the root CA | +| App Service managed cert | No — already trusted | + +## Key Vault Integration (Recommended) + +Store certificates in Azure Key Vault for automated rotation and centralized management. + +### Setup Steps + +```bash +# Step 1: Create managed identity for App Gateway +az network application-gateway identity assign \ + --gateway-name myAppGw -g myRG \ + --identity myAppGwIdentity + +# Step 2: Grant Key Vault access to the managed identity +az keyvault set-policy \ + --name myKeyVault \ + --object-id \ + --secret-permissions get list + +# Step 3: Reference Key Vault certificate in App Gateway +az network application-gateway ssl-cert create \ + --gateway-name myAppGw -g myRG \ + --name kvCert \ + --key-vault-secret-id "https://myKeyVault.vault.azure.net/secrets/myCert" +``` + +### Key Vault Certificate Rotation + +- Application Gateway polls Key Vault every **4 hours** for new certificate versions +- When a new version is detected, it's automatically deployed (no downtime) +- Use the **secret URI without version** to enable auto-rotation: + - ✅ `https://mykv.vault.azure.net/secrets/cert` (auto-rotates) + - ❌ `https://mykv.vault.azure.net/secrets/cert/abc123` (pinned to version) + +## SSL Policy Configuration + +SSL policies control which TLS protocol versions and cipher suites are accepted. + +### Predefined Policies + +| Policy | Min TLS | Cipher Suites | Recommendation | +|--------|---------|---------------|----------------| +| AppGwSslPolicy20220101 | 1.2 | Strong modern ciphers | ✅ Recommended | +| AppGwSslPolicy20220101S | 1.2 | Strictest (no CBC) | High-security workloads | +| AppGwSslPolicy20170401S | 1.2 | Good | Acceptable | +| AppGwSslPolicy20150501 | 1.0 | Legacy | ❌ Avoid | + +```bash +# Apply predefined SSL policy +az network application-gateway ssl-policy set \ + --gateway-name myAppGw -g myRG \ + --policy-type Predefined \ + --policy-name AppGwSslPolicy20220101 +``` + +### Custom SSL Policy + +```bash +az network application-gateway ssl-policy set \ + --gateway-name myAppGw -g myRG \ + --policy-type CustomV2 \ + --min-protocol-version TLSv1_2 \ + --cipher-suites \ + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 \ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +``` + +## Mutual TLS (mTLS) + +Client certificate authentication — the gateway verifies the client's certificate. + +### Configuration + +```bash +# Upload trusted client CA certificate +az network application-gateway ssl-profile create \ + --gateway-name myAppGw -g myRG \ + --name mtlsProfile \ + --client-auth-configuration verifyClientCertIssuerDN=true \ + --trusted-client-certificates clientRootCA + +# Associate SSL profile with listener +az network application-gateway http-listener update \ + --gateway-name myAppGw -g myRG \ + --name httpsListener \ + --ssl-profile mtlsProfile +``` + +### Client Certificate Header Forwarding + +Application Gateway forwards client certificate details to backends via headers: + +| Header | Content | +|--------|---------| +| `X-Forwarded-Client-Cert` | Base64-encoded client certificate | +| `X-Client-Cert-Issuer` | Certificate issuer DN | +| `X-Client-Cert-Subject` | Certificate subject DN | +| `X-Client-Cert-Serial` | Certificate serial number | + +## Troubleshooting SSL Issues + +| Symptom | Cause | Fix | +|---------|-------|-----| +| ERR_SSL_PROTOCOL_ERROR | TLS version mismatch | Update SSL policy to support client's TLS version | +| 502 with HTTPS backend | Backend cert not trusted | Upload backend root CA as trusted root cert | +| Certificate expiry warnings | Cert about to expire | Rotate in Key Vault; auto-deploys in 4 hours | +| mTLS handshake failure | Client cert not from trusted CA | Verify trusted client CA is uploaded correctly | +| Mixed content warnings | HTTP resources on HTTPS page | Use rewrite rules or fix application URLs | + +## Source Documentation + +- [SSL termination overview](https://learn.microsoft.com/azure/application-gateway/ssl-overview) +- [End-to-end TLS](https://learn.microsoft.com/azure/application-gateway/end-to-end-ssl-portal) +- [Key Vault certificates](https://learn.microsoft.com/azure/application-gateway/key-vault-certs) +- [SSL policy overview](https://learn.microsoft.com/azure/application-gateway/application-gateway-ssl-policy-overview) +- [Mutual authentication](https://learn.microsoft.com/azure/application-gateway/mutual-authentication-overview) diff --git a/plugin/skills/azure-application-gateway/references/url-routing.md b/plugin/skills/azure-application-gateway/references/url-routing.md new file mode 100644 index 000000000..a98817f5c --- /dev/null +++ b/plugin/skills/azure-application-gateway/references/url-routing.md @@ -0,0 +1,233 @@ +# URL-Based Routing, Multi-Site, Redirects, and Rewrites + +## Path-Based Routing + +Path-based routing sends requests to different backend pools based on the URL path. + +### Architecture Example + +``` +Client → App Gateway Listener (:443) + │ + URL Path Map + ├── /api/* → API Backend Pool (port 8080) + ├── /images/* → Blob Storage Backend (port 443) + ├── /admin/* → Admin Backend Pool (port 443) + └── /* (default) → Web Frontend Pool (port 80) +``` + +### Configuration + +```bash +# Create URL path map with rules +az network application-gateway url-path-map create \ + --gateway-name myAppGw -g myRG \ + --name myPathMap \ + --default-address-pool webPool \ + --default-http-settings defaultSettings \ + --paths "/api/*" \ + --address-pool apiPool \ + --http-settings apiSettings \ + --rule-name apiRule + +# Add additional path rules +az network application-gateway url-path-map rule create \ + --gateway-name myAppGw -g myRG \ + --path-map-name myPathMap \ + --name imagesRule \ + --paths "/images/*" "/static/*" \ + --address-pool staticPool \ + --http-settings staticSettings + +az network application-gateway url-path-map rule create \ + --gateway-name myAppGw -g myRG \ + --path-map-name myPathMap \ + --name adminRule \ + --paths "/admin/*" \ + --address-pool adminPool \ + --http-settings adminSettings +``` + +### Path Matching Rules + +| Pattern | Matches | Does Not Match | +|---------|---------|----------------| +| `/api/*` | `/api/users`, `/api/v1/data` | `/API/users` (case-sensitive) | +| `/images/*.jpg` | `/images/logo.jpg` | `/images/logo.png` | +| `/` | Exact root path | `/anything-else` | +| `/*` (default) | Everything not matched by other rules | — | + +**Important**: Path matching is **case-sensitive** and evaluated in order. The first match wins. The default rule (`/*`) catches everything not matched. + +## Multi-Site Hosting + +Host multiple websites on a single Application Gateway using hostname-based listeners. + +### Configuration + +```bash +# Listener for site 1 +az network application-gateway http-listener create \ + --gateway-name myAppGw -g myRG \ + --name contoso-listener \ + --frontend-ip appGatewayFrontendIP \ + --frontend-port port443 \ + --ssl-cert contoso-cert \ + --host-name "www.contoso.com" + +# Listener for site 2 +az network application-gateway http-listener create \ + --gateway-name myAppGw -g myRG \ + --name fabrikam-listener \ + --frontend-ip appGatewayFrontendIP \ + --frontend-port port443 \ + --ssl-cert fabrikam-cert \ + --host-name "www.fabrikam.com" + +# Routing rule for each site +az network application-gateway rule create \ + --gateway-name myAppGw -g myRG \ + --name contoso-rule --priority 100 \ + --rule-type Basic \ + --http-listener contoso-listener \ + --address-pool contosoPool \ + --http-settings contosoSettings + +az network application-gateway rule create \ + --gateway-name myAppGw -g myRG \ + --name fabrikam-rule --priority 200 \ + --rule-type Basic \ + --http-listener fabrikam-listener \ + --address-pool fabrikamPool \ + --http-settings fabrikamSettings +``` + +### Wildcard Hostnames + +v2 supports wildcard hostnames in listeners: + +| Pattern | Matches | +|---------|---------| +| `*.contoso.com` | `www.contoso.com`, `api.contoso.com` | +| `*.contoso.*` | Not supported (only leading wildcard) | + +## Redirect Configurations + +### HTTP to HTTPS Redirect + +```bash +# Create HTTPS listener first +az network application-gateway http-listener create \ + --gateway-name myAppGw -g myRG \ + --name httpsListener \ + --frontend-ip appGatewayFrontendIP \ + --frontend-port port443 \ + --ssl-cert myCert + +# Create redirect config +az network application-gateway redirect-config create \ + --gateway-name myAppGw -g myRG \ + --name httpToHttps \ + --type Permanent \ + --target-listener httpsListener \ + --include-path true \ + --include-query-string true + +# Create HTTP listener and rule that uses the redirect +az network application-gateway http-listener create \ + --gateway-name myAppGw -g myRG \ + --name httpListener \ + --frontend-ip appGatewayFrontendIP \ + --frontend-port port80 + +az network application-gateway rule create \ + --gateway-name myAppGw -g myRG \ + --name redirectRule --priority 50 \ + --rule-type Basic \ + --http-listener httpListener \ + --redirect-config httpToHttps +``` + +### Redirect Types + +| Type | HTTP Code | Use Case | +|------|-----------|----------| +| Permanent | 301 | HTTP → HTTPS (cached by browsers) | +| Found | 302 | Temporary redirect | +| SeeOther | 303 | POST → GET redirect | +| Temporary | 307 | Temporary, preserves HTTP method | + +### External URL Redirect + +```bash +az network application-gateway redirect-config create \ + --gateway-name myAppGw -g myRG \ + --name externalRedirect \ + --type Permanent \ + --target-url "https://www.newsite.com" \ + --include-path true \ + --include-query-string true +``` + +## Rewrite Rules + +Rewrite rules modify HTTP request and response headers or URL components. + +### Common Rewrite Scenarios + +| Scenario | Action | +|----------|--------| +| Add security headers | Add `X-Frame-Options`, `Strict-Transport-Security` to response | +| Strip server headers | Remove `Server`, `X-Powered-By` from response | +| Modify URL path | Rewrite `/old-api/v1/*` to `/api/v2/*` | +| Add correlation header | Add `X-Request-ID` from `{var_request_uri}` | +| Override host header | Set `Host` header for backend routing | + +### Create Rewrite Rule Set + +```bash +# Create rule set +az network application-gateway rewrite-rule set create \ + --gateway-name myAppGw -g myRG \ + --name securityHeaders + +# Add rule: Security response headers +az network application-gateway rewrite-rule create \ + --gateway-name myAppGw -g myRG \ + --rule-set-name securityHeaders \ + --name addSecurityHeaders \ + --response-headers "X-Frame-Options=SAMEORIGIN" "Strict-Transport-Security=max-age=31536000; includeSubDomains" "X-Content-Type-Options=nosniff" + +# Add rule: Remove server identification headers +az network application-gateway rewrite-rule create \ + --gateway-name myAppGw -g myRG \ + --rule-set-name securityHeaders \ + --name removeServerHeaders \ + --response-headers "Server=" "X-Powered-By=" + +# Associate rewrite rule set with routing rule +az network application-gateway rule update \ + --gateway-name myAppGw -g myRG \ + --name myRule \ + --rewrite-rule-set securityHeaders +``` + +### Server Variables for Conditions + +| Variable | Description | +|----------|-------------| +| `{var_host}` | Host header value | +| `{var_request_uri}` | Full request URI | +| `{var_uri_path}` | URI path only | +| `{var_query_string}` | Query string | +| `{var_client_ip}` | Client IP address | +| `{var_server_port}` | Server port | +| `{http_req_headerName}` | Request header value | +| `{http_resp_headerName}` | Response header value | + +## Source Documentation + +- [URL path-based routing](https://learn.microsoft.com/azure/application-gateway/url-route-overview) +- [Multi-site hosting](https://learn.microsoft.com/azure/application-gateway/multiple-site-overview) +- [Redirect overview](https://learn.microsoft.com/azure/application-gateway/redirect-overview) +- [Rewrite HTTP headers and URL](https://learn.microsoft.com/azure/application-gateway/rewrite-http-headers-url) diff --git a/plugin/skills/azure-application-gateway/references/waf-integration.md b/plugin/skills/azure-application-gateway/references/waf-integration.md new file mode 100644 index 000000000..ac699f702 --- /dev/null +++ b/plugin/skills/azure-application-gateway/references/waf-integration.md @@ -0,0 +1,222 @@ +# WAF v2 Integration on Application Gateway + +## Overview + +Web Application Firewall (WAF) v2 runs as a tier of Application Gateway (WAF_v2 SKU), providing protection against common web exploits and vulnerabilities using OWASP Core Rule Set (CRS) and Microsoft-managed bot protection rules. + +## Enabling WAF + +### New Application Gateway with WAF + +```bash +# Create WAF policy +az network application-gateway waf-policy create \ + --name myWafPolicy -g myRG + +# Create Application Gateway with WAF_v2 SKU +az network application-gateway create \ + --name myWafAppGw -g myRG \ + --sku WAF_v2 \ + --capacity 2 \ + --vnet-name myVNet \ + --subnet AppGwSubnet \ + --public-ip-address wafPIP \ + --waf-policy myWafPolicy +``` + +### Add WAF to Existing Application Gateway + +You cannot change SKU from Standard_v2 to WAF_v2 in-place. Options: +1. **Create new** WAF_v2 gateway and migrate configuration +2. **Associate WAF policy** with per-listener scope on Standard_v2 (limited) + +For full WAF capabilities, deploy as WAF_v2 from the start. + +## WAF Modes + +| Mode | Behavior | When to Use | +|------|----------|-------------| +| Detection | Logs rule matches but does NOT block requests | Initial deployment, tuning phase | +| Prevention | Blocks requests matching rules, returns 403 | Production after tuning is complete | + +```bash +# Set to Detection mode (for tuning) +az network application-gateway waf-policy policy-setting update \ + --policy-name myWafPolicy -g myRG \ + --mode Detection \ + --state Enabled + +# Switch to Prevention mode (production) +az network application-gateway waf-policy policy-setting update \ + --policy-name myWafPolicy -g myRG \ + --mode Prevention \ + --state Enabled +``` + +## Managed Rule Sets + +### OWASP Core Rule Set (CRS) + +| Version | Status | Recommendation | +|---------|--------|----------------| +| CRS 3.2 | Current | ✅ Recommended for most deployments | +| CRS 3.1 | Supported | Stable, well-tested | +| CRS 3.0 | Supported | Legacy | +| CRS 2.2.9 | Deprecated | ❌ Migrate | + +```bash +# Configure CRS 3.2 +az network application-gateway waf-policy managed-rule rule-set add \ + --policy-name myWafPolicy -g myRG \ + --type OWASP \ + --version 3.2 +``` + +### CRS Rule Groups + +| Group | Protects Against | +|-------|-----------------| +| SQL Injection (sqli) | SQL injection attacks | +| Cross-Site Scripting (xss) | XSS attacks | +| Local File Inclusion (lfi) | File traversal attacks | +| Remote File Inclusion (rfi) | Remote file inclusion | +| Remote Code Execution (rce) | Command injection | +| Protocol Enforcement | HTTP protocol violations | +| Protocol Attack | Request smuggling, splitting | +| Session Fixation | Session hijacking | +| Scanner Detection | Vulnerability scanner fingerprints | +| General | General security rules | + +### Bot Manager Rule Set + +```bash +# Add bot manager rules +az network application-gateway waf-policy managed-rule rule-set add \ + --policy-name myWafPolicy -g myRG \ + --type Microsoft_BotManagerRuleSet \ + --version 1.0 +``` + +## Custom Rules + +Custom rules are evaluated BEFORE managed rules with higher priority. + +### Rule Components + +| Component | Options | +|-----------|---------| +| Priority | 1-100 (lower = evaluated first) | +| Rule type | MatchRule, RateLimitRule | +| Match conditions | IP address, geo, request body, headers, URI, query string | +| Action | Allow, Block, Log, AnomalyScoring | +| Operators | IPMatch, GeoMatch, Equal, Contains, BeginsWith, Regex, etc. | + +### Example: Block by IP + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name myWafPolicy -g myRG \ + --name blockBadIP \ + --priority 10 \ + --rule-type MatchRule \ + --action Block \ + --match-condition \ + match-variable=RemoteAddr \ + operator=IPMatch \ + values="203.0.113.0/24" "198.51.100.50" +``` + +### Example: Geo-Block + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name myWafPolicy -g myRG \ + --name geoBlock \ + --priority 20 \ + --rule-type MatchRule \ + --action Block \ + --match-condition \ + match-variable=RemoteAddr \ + operator=GeoMatch \ + values="CN" "RU" +``` + +### Example: Rate Limiting + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name myWafPolicy -g myRG \ + --name rateLimit \ + --priority 30 \ + --rule-type RateLimitRule \ + --action Block \ + --rate-limit-threshold 100 \ + --rate-limit-duration FiveMins \ + --group-by-user-session "client_addr" \ + --match-condition \ + match-variable=RequestUri \ + operator=Contains \ + values="/api/" +``` + +## Exclusions + +When managed rules produce false positives, add exclusions instead of disabling entire rule groups. + +### Exclusion Scopes + +| Scope | Description | +|-------|-------------| +| Request header name | Exclude a specific header from inspection | +| Request cookie name | Exclude a specific cookie | +| Request body post arg | Exclude a form field | +| Request body JSON arg | Exclude a JSON property | + +```bash +# Exclude a specific field from a specific rule +az network application-gateway waf-policy managed-rule exclusion add \ + --policy-name myWafPolicy -g myRG \ + --match-variable RequestBodyPostArgsNames \ + --selector-match-operator Equals \ + --selector "description" \ + --exclusion-rule-set-type OWASP \ + --exclusion-rule-set-version 3.2 \ + --exclusion-rule-group REQUEST-942-APPLICATION-ATTACK-SQLI \ + --exclusion-rules 942130 +``` + +## WAF Tuning Workflow + +1. **Deploy in Detection mode** — enable WAF but don't block +2. **Monitor logs** — review WAF logs for triggered rules +3. **Identify false positives** — legitimate requests flagged as attacks +4. **Add exclusions** — for specific fields/rules causing false positives +5. **Disable noisy rules** — only as a last resort (prefer exclusions) +6. **Switch to Prevention mode** — when false positives are minimized +7. **Continue monitoring** — WAF tuning is ongoing + +### Monitoring WAF + +```bash +# Enable diagnostic logging +az monitor diagnostic-settings create \ + --name wafDiag \ + --resource \ + --workspace \ + --logs '[{"category":"ApplicationGatewayFirewallLog","enabled":true}]' +``` + +Key log fields to analyze: +- `ruleId` — which rule fired +- `action` — Detected, Blocked, Matched +- `message` — rule description +- `requestUri` — request that triggered the rule +- `details.data` — the specific data that matched + +## Source Documentation + +- [WAF on Application Gateway overview](https://learn.microsoft.com/azure/web-application-firewall/ag/ag-overview) +- [WAF custom rules](https://learn.microsoft.com/azure/web-application-firewall/ag/create-custom-waf-rules) +- [WAF exclusion lists](https://learn.microsoft.com/azure/web-application-firewall/ag/application-gateway-waf-configuration) +- [WAF tuning](https://learn.microsoft.com/azure/web-application-firewall/ag/application-gateway-waf-faq) +- [CRS rule groups](https://learn.microsoft.com/azure/web-application-firewall/ag/application-gateway-crs-rulegroups-rules) diff --git a/plugin/skills/azure-bastion/SKILL.md b/plugin/skills/azure-bastion/SKILL.md new file mode 100644 index 000000000..e839e428d --- /dev/null +++ b/plugin/skills/azure-bastion/SKILL.md @@ -0,0 +1,109 @@ +--- +name: azure-bastion +description: "Deploy and configure Azure Bastion for secure RDP/SSH access to Azure VMs without public IP exposure, including Developer, Basic, Standard, and Premium SKUs. WHEN: bastion, connect to VM, RDP without public IP, SSH securely, remote access VM, AzureBastionSubnet. DO NOT USE FOR: VPN site-to-site connectivity (use azure-vpn-gateway), application-level load balancing (use azure-application-gateway), network security rules (use azure-virtual-network)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Bastion Skill + +## When to Use This Skill + +- User wants to connect to a VM via RDP or SSH without exposing a public IP +- User asks about Azure Bastion deployment or SKU selection +- User needs to configure the AzureBastionSubnet +- User wants to use native RDP/SSH clients with Bastion (tunnel mode) +- User asks about shareable links for VM access +- User needs to upgrade or downgrade a Bastion SKU +- User wants to troubleshoot Bastion connectivity issues + +## Rules + +1. The subnet MUST be named exactly `AzureBastionSubnet` — any other name will fail deployment. +2. AzureBastionSubnet requires a /26 or larger prefix (minimum 64 addresses) for Basic/Standard/Premium SKUs. +3. Developer SKU can use a /26 subnet and does not require a public IP, but only supports one concurrent connection. +4. Do NOT place NSG on AzureBastionSubnet unless using specific required rules — Bastion manages its own security. +5. Standard and Premium SKUs support native client connections, shareable links, and host scaling. +6. Basic SKU does NOT support native client, shareable links, or IP-based connections — recommend Standard for most production use. +7. You can upgrade from Basic → Standard → Premium, but you CANNOT downgrade SKUs. +8. Bastion uses TLS over port 443 — ensure outbound 443 is allowed from the client browser. +9. For native client RDP/SSH, users must install Azure CLI and use `az network bastion rdp` or `az network bastion ssh`. +10. Bastion connects to VMs using private IPs — VMs do NOT need public IPs. + +## MCP Tools + +> Azure Bastion has limited MCP tool support. Use CLI commands for all operations. + +## CLI Fallback + +```bash +# Create AzureBastionSubnet +az network vnet subnet create -g MyRG --vnet-name MyVNet -n AzureBastionSubnet \ + --address-prefix 10.0.255.0/26 + +# Create public IP for Bastion (Standard SKU required) +az network public-ip create -g MyRG -n BastionPublicIP --sku Standard --allocation-method Static + +# Create Bastion host (Standard SKU) +az network bastion create -g MyRG -n MyBastion --vnet-name MyVNet \ + --public-ip-address BastionPublicIP --sku Standard + +# Create Bastion host (Developer SKU — no public IP needed) +az network bastion create -g MyRG -n MyBastionDev --vnet-name MyVNet --sku Developer + +# Connect via native RDP client +az network bastion rdp -g MyRG -n MyBastion --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyVM + +# Connect via native SSH client +az network bastion ssh -g MyRG -n MyBastion --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyVM \ + --auth-type ssh-key --ssh-key ~/.ssh/id_rsa + +# Create tunnel for custom port forwarding +az network bastion tunnel -g MyRG -n MyBastion --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyVM \ + --resource-port 3389 --port 50001 + +# Show Bastion details +az network bastion show -g MyRG -n MyBastion +az network bastion list -g MyRG -o table + +# Update Bastion scale units (Standard/Premium only) +az network bastion update -g MyRG -n MyBastion --scale-units 4 + +# Enable shareable links (Standard/Premium only) +az network bastion update -g MyRG -n MyBastion --enable-shareable-link true +``` + +## Key Concepts + +### SKU Comparison + +| Feature | Developer | Basic | Standard | Premium | +|---------|-----------|-------|----------|---------| +| Concurrent sessions | 1 | 25+ | 50+ (scales) | 50+ (scales) | +| Public IP required | No | Yes | Yes | Yes | +| AzureBastionSubnet size | /26 | /26 | /26 | /26 | +| Native client support | No | No | Yes | Yes | +| Shareable links | No | No | Yes | Yes | +| Host scaling | No | No | Yes (2-50 units) | Yes (2-50 units) | +| IP-based connection | No | No | Yes | Yes | +| Kerberos authentication | No | No | Yes | Yes | +| Session recording | No | No | No | Yes | +| Private-only deployment | No | No | No | Yes | +| SKU upgrade path | → Basic | → Standard | → Premium | — | + +### Scale Units and Sessions + +| Scale Units | Concurrent RDP | Concurrent SSH | +|-------------|---------------|----------------| +| 2 (default) | 20 | 40 | +| 4 | 40 | 80 | +| 10 | 100 | 200 | +| 50 (max) | 500 | 1000 | + +## References + +- [Bastion SKU Comparison](references/bastion-skus.md) +- [Native Client Guide](references/native-client.md) +- [Shareable Links](references/shareable-links.md) diff --git a/plugin/skills/azure-bastion/references/bastion-skus.md b/plugin/skills/azure-bastion/references/bastion-skus.md new file mode 100644 index 000000000..888bbffd8 --- /dev/null +++ b/plugin/skills/azure-bastion/references/bastion-skus.md @@ -0,0 +1,217 @@ +# Azure Bastion SKU Comparison + +Azure Bastion offers four SKUs — Developer, Basic, Standard, and Premium — each designed for different scale and feature requirements. Choosing the right SKU depends on concurrent session needs, whether native client access is required, and compliance features like session recording. + +## SKU Overview + +### Developer SKU + +The Developer SKU is a free-tier option intended for dev/test environments and individual developers. + +**Use cases:** +- Single developer needing occasional VM access during development +- Lab or sandbox environments where only one person connects at a time +- Cost-sensitive scenarios where Bastion features beyond browser-based RDP/SSH are unnecessary + +**Key characteristics:** +- Supports only **one concurrent connection** at a time +- Does **not** require a public IP address — deploys as a fully private resource +- Deployed per virtual network, not per subnet in the traditional sense (still uses AzureBastionSubnet) +- No host scaling — fixed at a single instance +- Browser-based RDP/SSH only — no native client, no tunnel support +- No shareable links, no IP-based connections, no Kerberos authentication +- Cannot be used in production workloads that need concurrent access + +**Deployment:** +```bash +# Developer SKU — no public IP parameter needed +az network bastion create -g MyRG -n MyBastionDev --vnet-name MyVNet --sku Developer +``` + +### Basic SKU + +The Basic SKU is the entry-level production SKU for teams that only need browser-based RDP/SSH. + +**Use cases:** +- Small teams accessing VMs through the Azure portal +- Environments where native client tools are not required +- Workloads where 25+ concurrent sessions are sufficient + +**Key characteristics:** +- Supports **25+ concurrent RDP sessions** and **25+ concurrent SSH sessions** with default 2 scale units +- Requires a **Standard SKU public IP** with static allocation +- Browser-based RDP/SSH via the Azure portal +- Does **not** support native client connections (`az network bastion rdp/ssh/tunnel`) +- Does **not** support shareable links +- Does **not** support IP-based connections (must target by Azure resource ID) +- Does **not** support host scaling — fixed at 2 scale units +- Does **not** support Kerberos authentication +- Can be upgraded to Standard (one-way, no downgrade) + +**Deployment:** +```bash +az network public-ip create -g MyRG -n BastionPIP --sku Standard --allocation-method Static +az network bastion create -g MyRG -n MyBastion --vnet-name MyVNet \ + --public-ip-address BastionPIP --sku Basic +``` + +### Standard SKU + +The Standard SKU is the recommended choice for most production environments. It unlocks native client support, shareable links, host scaling, and IP-based connections. + +**Use cases:** +- Production environments needing native RDP/SSH client access +- Teams that need shareable link access for contractors or support staff +- High-concurrency environments requiring host scaling (2–50 scale units) +- Scenarios requiring IP-based connections to non-Azure or on-premises VMs reachable via the VNet +- Kerberos authentication for domain-joined VMs + +**Key characteristics:** +- All Basic SKU features, plus: +- **Native client support** — use `az network bastion rdp`, `az network bastion ssh`, and `az network bastion tunnel` +- **Shareable links** — generate URLs for VM access without portal login +- **Host scaling** — scale from 2 to 50 scale units to handle more concurrent sessions +- **IP-based connection** — connect to VMs by private IP address, not just Azure resource ID +- **Kerberos authentication** — single sign-on for domain-joined Windows VMs +- Can be upgraded to Premium (one-way, no downgrade) + +**Deployment:** +```bash +az network public-ip create -g MyRG -n BastionPIP --sku Standard --allocation-method Static +az network bastion create -g MyRG -n MyBastion --vnet-name MyVNet \ + --public-ip-address BastionPIP --sku Standard +``` + +### Premium SKU + +The Premium SKU adds enterprise compliance and enhanced security features on top of Standard. + +**Use cases:** +- Regulated industries requiring session recording for audit trails +- Zero-trust architectures needing private-only Bastion deployment (no public IP exposure) +- Organizations with strict compliance requirements (HIPAA, PCI-DSS, SOC 2) + +**Key characteristics:** +- All Standard SKU features, plus: +- **Session recording** — record RDP/SSH sessions to a storage account for audit and compliance +- **Private-only deployment** — deploy Bastion without a public IP; access through private endpoints or ExpressRoute/VPN +- Highest tier — no further upgrade path available + +**Deployment:** +```bash +az network public-ip create -g MyRG -n BastionPIP --sku Standard --allocation-method Static +az network bastion create -g MyRG -n MyBastion --vnet-name MyVNet \ + --public-ip-address BastionPIP --sku Premium +``` + +## Full Feature Comparison + +| Feature | Developer | Basic | Standard | Premium | +|---------|-----------|-------|----------|---------| +| Concurrent RDP sessions | 1 | 20 (2 units) | Scales with units | Scales with units | +| Concurrent SSH sessions | 1 | 40 (2 units) | Scales with units | Scales with units | +| Public IP required | No | Yes (Standard SKU) | Yes (Standard SKU) | Optional | +| AzureBastionSubnet required | Yes (/26) | Yes (/26+) | Yes (/26+) | Yes (/26+) | +| Browser-based RDP/SSH | Yes | Yes | Yes | Yes | +| Native client (CLI tunnel) | No | No | Yes | Yes | +| Shareable links | No | No | Yes | Yes | +| Host scaling (2–50 units) | No | No | Yes | Yes | +| IP-based connection | No | No | Yes | Yes | +| Kerberos authentication | No | No | Yes | Yes | +| Session recording | No | No | No | Yes | +| Private-only deployment | No | No | No | Yes | +| File transfer (browser) | No | Yes | Yes | Yes | +| Copy/paste in browser | Yes | Yes | Yes | Yes | + +## SKU Upgrade Path + +Upgrades are **one-way only** — you cannot downgrade a SKU once upgraded. + +``` +Developer → Basic → Standard → Premium +``` + +- **Developer → Basic**: Requires adding a Standard SKU public IP and re-deploying. +- **Basic → Standard**: In-place upgrade via `az network bastion update` or Azure portal. No downtime for existing connections, but new features become available immediately. +- **Standard → Premium**: In-place upgrade. Session recording and private-only options become available. + +```bash +# Upgrade Basic to Standard +az network bastion update -g MyRG -n MyBastion --sku Standard + +# Upgrade Standard to Premium +az network bastion update -g MyRG -n MyBastion --sku Premium +``` + +> **Warning:** If you upgrade from Basic to Standard and later decide Standard features are unnecessary, you cannot revert to Basic. Plan SKU selection carefully. + +## Pricing Considerations + +Bastion pricing is based on two components: + +1. **Hourly charge per deployment** — billed per Bastion host, varies by SKU tier +2. **Data transfer (outbound)** — standard Azure egress charges apply + +**Scale units affect cost directly:** +- Each scale unit adds incremental hourly cost (Standard and Premium only) +- Default is 2 scale units; increasing to 10 or 50 units multiplies the per-unit hourly rate +- Developer SKU has the lowest cost (often free-tier eligible for limited hours) +- Basic SKU is fixed at 2 units — no scaling, predictable cost + +**Cost optimization tips:** +- Use Developer SKU for dev/test to minimize cost +- Start with 2 scale units in Standard/Premium and scale up only when concurrency demands it +- Delete Bastion hosts in non-production environments when not in use — Bastion billing is hourly +- Consider a single Bastion host with VNet peering to serve multiple VNets + +## Subnet and Public IP Requirements + +All SKUs require a subnet named **exactly** `AzureBastionSubnet`: +- Minimum prefix: **/26** (64 addresses) — applies to all SKUs +- Recommended: **/26** is sufficient for most deployments; use /25 or larger only if deploying many scale units +- The subnet must not contain any other resources (no VMs, NICs, or other services) +- No UDRs (User Defined Routes) are supported on AzureBastionSubnet + +**Public IP requirements:** +- Developer SKU: No public IP required +- Basic, Standard: Standard SKU public IP with static allocation is **mandatory** +- Premium: Public IP is optional (supports private-only deployment) + +## NSG Rules on AzureBastionSubnet + +By default, do **not** apply an NSG to AzureBastionSubnet — Bastion manages its own security. If organizational policy requires an NSG, the following rules are mandatory: + +**Inbound rules (required):** +| Priority | Source | Port | Destination | Port | Protocol | Action | +|----------|--------|------|-------------|------|----------|--------| +| 120 | Internet | * | * | 443 | TCP | Allow | +| 130 | GatewayManager | * | * | 443 | TCP | Allow | +| 140 | AzureLoadBalancer | * | * | 443 | TCP | Allow | +| 150 | VirtualNetwork | * | * | 8080, 5701 | Any | Allow | + +**Outbound rules (required):** +| Priority | Source | Port | Destination | Port | Protocol | Action | +|----------|--------|------|-------------|------|----------|--------| +| 120 | * | * | VirtualNetwork | 22, 3389 | Any | Allow | +| 130 | * | * | AzureCloud | 443 | TCP | Allow | +| 140 | * | * | Internet | 80 | TCP | Allow | + +> **Critical:** Omitting any of these rules will break Bastion connectivity. The GatewayManager inbound rule is required for the Bastion control plane. + +## Decision Tree: Choosing a SKU + +``` +Need VM access for development/testing only? +├── Yes, single user → Developer SKU +└── No, production use + ├── Browser-only access is sufficient, <25 concurrent users → Basic SKU + └── Need native client, scaling, or shareable links? + ├── Yes, no session recording needed → Standard SKU + └── Need session recording or private-only deployment → Premium SKU +``` + +**Quick recommendation:** +- **Developer** — solo dev/test, budget-conscious +- **Basic** — small team, browser-only, simple setup +- **Standard** — most production workloads (recommended default) +- **Premium** — regulated environments with audit requirements diff --git a/plugin/skills/azure-bastion/references/native-client.md b/plugin/skills/azure-bastion/references/native-client.md new file mode 100644 index 000000000..d710add33 --- /dev/null +++ b/plugin/skills/azure-bastion/references/native-client.md @@ -0,0 +1,261 @@ +# Azure Bastion Native Client Support + +Native client support allows you to connect to Azure VMs using your local RDP and SSH applications — such as Windows Remote Desktop (`mstsc.exe`), PuTTY, OpenSSH, or any SSH client — through an Azure Bastion tunnel, instead of relying on the browser-based portal experience. + +## Overview + +By default, Azure Bastion provides browser-based RDP/SSH from the Azure portal. Native client support extends this by creating an encrypted tunnel from your workstation through the Bastion host to the target VM. Your local client application communicates through this tunnel, giving you full-featured RDP/SSH with local clipboard, drive redirection, audio, and multi-monitor support. + +**Key benefit:** You get the security of Bastion (no public IP on VMs, TLS-encrypted traffic, Azure AD integration) combined with the full feature set of your native RDP/SSH client. + +## Prerequisites + +1. **Azure Bastion SKU:** Standard or Premium — native client is **not** available on Developer or Basic SKUs +2. **Azure CLI:** Version 2.32.0 or later installed on your workstation +3. **SSH extension (for SSH):** Install via `az extension add --name ssh` +4. **Network access:** Your workstation must have outbound access on port 443 to the Bastion host +5. **Azure authentication:** You must be signed in with `az login` and have appropriate RBAC permissions (Reader role on the VM, Bastion, VNet, and NIC resources) + +```bash +# Verify Azure CLI version +az --version + +# Install or update the SSH extension +az extension add --name ssh --upgrade + +# Sign in to Azure +az login +``` + +## az network bastion rdp + +Use `az network bastion rdp` to connect to a Windows VM using your native RDP client (mstsc.exe on Windows). + +### Basic Usage + +```bash +# Connect by target resource ID +az network bastion rdp \ + --name MyBastion \ + --resource-group MyRG \ + --target-resource-id /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyWindowsVM +``` + +### Connect by IP Address (Standard/Premium) + +```bash +# Connect to a VM by its private IP (requires IP-based connection enabled) +az network bastion rdp \ + --name MyBastion \ + --resource-group MyRG \ + --target-ip-address 10.0.1.4 +``` + +### Full Parameter Reference + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `--name` / `-n` | Yes | Name of the Bastion host | +| `--resource-group` / `-g` | Yes | Resource group containing the Bastion host | +| `--target-resource-id` | Yes* | Full ARM resource ID of the target VM | +| `--target-ip-address` | Yes* | Private IP of the target VM (requires IP-based connection) | +| `--configure` | No | Configure RDP file settings before connecting | +| `--disable-gateway` | No | Disable RD Gateway usage | + +> *Provide either `--target-resource-id` or `--target-ip-address`, not both. + +### What Happens Under the Hood + +1. Azure CLI authenticates with your Azure credentials +2. A secure WebSocket tunnel is established from your machine to the Bastion host over port 443 +3. Bastion forwards the connection to the target VM's private IP on port 3389 +4. Your local `mstsc.exe` (Remote Desktop) launches and connects through the tunnel +5. You authenticate to the VM with Windows credentials (or Kerberos SSO if configured) + +## az network bastion ssh + +Use `az network bastion ssh` to connect to a Linux (or Windows with OpenSSH) VM using your local SSH client. + +### Connect with SSH Key + +```bash +az network bastion ssh \ + --name MyBastion \ + --resource-group MyRG \ + --target-resource-id /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyLinuxVM \ + --auth-type ssh-key \ + --username azureuser \ + --ssh-key ~/.ssh/id_rsa +``` + +### Connect with Password + +```bash +az network bastion ssh \ + --name MyBastion \ + --resource-group MyRG \ + --target-resource-id /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyLinuxVM \ + --auth-type password \ + --username azureuser +# You will be prompted for the password interactively +``` + +### Connect with Azure AD Authentication + +Azure AD (Entra ID) authentication allows passwordless SSH using your Azure identity. The target VM must have the AADSSHLoginForLinux extension installed. + +```bash +# Install the AAD SSH login extension on the VM (one-time setup) +az vm extension set \ + --publisher Microsoft.Azure.ActiveDirectory \ + --name AADSSHLoginForLinux \ + --resource-group MyRG \ + --vm-name MyLinuxVM + +# Connect using Azure AD auth +az network bastion ssh \ + --name MyBastion \ + --resource-group MyRG \ + --target-resource-id /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyLinuxVM \ + --auth-type AAD +``` + +**Required RBAC for AAD SSH:** +- `Virtual Machine Administrator Login` — full sudo access +- `Virtual Machine User Login` — standard user access + +### Full Parameter Reference + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `--name` / `-n` | Yes | Name of the Bastion host | +| `--resource-group` / `-g` | Yes | Resource group containing the Bastion host | +| `--target-resource-id` | Yes* | Full ARM resource ID of the target VM | +| `--target-ip-address` | Yes* | Private IP of the target VM | +| `--auth-type` | Yes | Authentication type: `ssh-key`, `password`, or `AAD` | +| `--username` | Cond. | SSH username (required for `ssh-key` and `password` auth) | +| `--ssh-key` | Cond. | Path to SSH private key file (required for `ssh-key` auth) | + +## az network bastion tunnel + +The tunnel command creates a local port forward through Bastion, allowing any local application to communicate with the target VM's port. This is the most flexible native client option. + +### Basic Tunnel for RDP + +```bash +# Forward local port 50001 to VM's port 3389 (RDP) +az network bastion tunnel \ + --name MyBastion \ + --resource-group MyRG \ + --target-resource-id /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyVM \ + --resource-port 3389 \ + --port 50001 +``` + +Then connect your RDP client to `localhost:50001`. + +### Tunnel for SSH + +```bash +# Forward local port 50022 to VM's port 22 (SSH) +az network bastion tunnel \ + --name MyBastion \ + --resource-group MyRG \ + --target-resource-id /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyLinuxVM \ + --resource-port 22 \ + --port 50022 +``` + +Then connect: `ssh -p 50022 azureuser@localhost` + +### Tunnel for Database Access + +```bash +# Forward local port 54321 to VM's PostgreSQL port 5432 +az network bastion tunnel \ + --name MyBastion \ + --resource-group MyRG \ + --target-resource-id /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyDBServer \ + --resource-port 5432 \ + --port 54321 +``` + +Then connect with psql: `psql -h localhost -p 54321 -U dbadmin mydb` + +### Full Parameter Reference + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `--name` / `-n` | Yes | Name of the Bastion host | +| `--resource-group` / `-g` | Yes | Resource group containing the Bastion host | +| `--target-resource-id` | Yes* | Full ARM resource ID of the target VM | +| `--target-ip-address` | Yes* | Private IP of the target VM | +| `--resource-port` | Yes | Port on the target VM to connect to | +| `--port` | Yes | Local port on your workstation to listen on | +| `--timeout` | No | Timeout in seconds for idle tunnel (default: no timeout) | + +## Use Cases + +### File Transfer via RDP +Native RDP supports drive redirection — map a local drive to the remote session for file transfer. This is not available in browser-based Bastion RDP. + +### Using PuTTY or Other SSH Clients +Create a tunnel with `az network bastion tunnel` on port 22, then point PuTTY at `localhost:`. This lets you use PuTTY's saved sessions, key agent, and X11 forwarding. + +### Forwarding Database Ports +Tunnel arbitrary ports (MySQL 3306, PostgreSQL 5432, SQL Server 1433) to access databases running on VMs without exposing them publicly. + +### SCP / SFTP File Transfer +With a tunnel active on port 22, use `scp` or `sftp` through `localhost:` for file operations. + +## Comparison: Browser-Based vs. Native Client + +| Feature | Browser-Based | Native Client | +|---------|--------------|---------------| +| SKU required | All SKUs | Standard / Premium | +| Clipboard copy/paste | Text only | Full (depends on client) | +| Drive/file redirection | No | Yes (RDP) | +| Multi-monitor | No | Yes (RDP) | +| Audio redirection | No | Yes (RDP) | +| Arbitrary port forwarding | No | Yes (tunnel) | +| AAD/Entra ID SSH login | No | Yes | +| Performance | Good | Better (native rendering) | +| Requires Azure CLI | No | Yes | +| Works from any browser | Yes | No (requires CLI + client) | + +## Troubleshooting + +### Tunnel Not Connecting + +1. **Verify SKU:** Native client requires Standard or Premium. Check with: + ```bash + az network bastion show -g MyRG -n MyBastion --query sku.name -o tsv + ``` +2. **Check Azure CLI version:** Must be 2.32.0+. Run `az --version`. +3. **Port conflict:** Ensure the local port is not already in use. Try a different `--port` value. +4. **Firewall:** Your workstation must allow outbound 443 to the Bastion public IP. + +### Authentication Failures + +1. **RBAC permissions:** You need at minimum Reader on the VM, NIC, Bastion, and VNet resources. +2. **AAD SSH:** Ensure the `AADSSHLoginForLinux` extension is installed and healthy on the VM. +3. **SSH key mismatch:** Verify the private key matches the public key configured on the VM. +4. **Expired token:** Run `az login` again if your Azure session has expired. + +### Timeout / Disconnection + +1. **Idle timeout:** Bastion tunnels may disconnect after extended idle periods. Keep the session active or re-establish the tunnel. +2. **Scale units:** If many users are connecting simultaneously, the Bastion host may be at capacity. Increase scale units: + ```bash + az network bastion update -g MyRG -n MyBastion --scale-units 4 + ``` +3. **Network instability:** Bastion uses WebSocket over TLS. Unstable internet connections or proxy servers that interfere with WebSocket can cause drops. + +### Common Error Messages + +| Error | Cause | Fix | +|-------|-------|-----| +| `Bastion Host SKU does not support native client` | Basic or Developer SKU | Upgrade to Standard | +| `Target resource not found` | Incorrect resource ID | Verify the full ARM resource ID | +| `Port already in use` | Local port conflict | Choose a different `--port` | +| `Authorization failed` | Missing RBAC permissions | Grant Reader role on required resources | diff --git a/plugin/skills/azure-bastion/references/shareable-links.md b/plugin/skills/azure-bastion/references/shareable-links.md new file mode 100644 index 000000000..84438d566 --- /dev/null +++ b/plugin/skills/azure-bastion/references/shareable-links.md @@ -0,0 +1,231 @@ +# Azure Bastion Shareable Links + +Shareable links allow you to generate a unique URL that provides browser-based RDP or SSH access to a specific Azure VM through Bastion — without requiring the recipient to navigate the Azure portal or have Azure portal access. + +## Overview + +When you create a shareable link, Azure Bastion generates a unique, cryptographically random URL that points directly to a Bastion-mediated connection for a target VM. Anyone with the link and valid VM credentials can connect through their web browser. + +**Key characteristics:** +- Access is browser-based only — no native client support through shareable links +- Recipients must still authenticate to the target VM (Windows credentials for RDP, SSH credentials for SSH) +- Links are tied to a specific VM and Bastion host +- The feature must be explicitly enabled on the Bastion host + +## Requirements + +- **Bastion SKU:** Standard or Premium — shareable links are **not** available on Developer or Basic SKUs +- **Feature enabled:** Shareable links must be enabled on the Bastion host before links can be generated +- **RBAC:** The person generating the link needs Contributor or Owner on the Bastion resource +- **VM state:** The target VM must be running when the link recipient connects + +## Enabling Shareable Links + +Shareable links are disabled by default. You must enable the feature on the Bastion host before creating any links. + +### Azure CLI + +```bash +# Enable shareable links on an existing Bastion host +az network bastion update \ + --resource-group MyRG \ + --name MyBastion \ + --enable-shareable-link true +``` + +### Azure Portal + +1. Navigate to your Bastion resource in the Azure portal +2. Go to **Configuration** in the left menu +3. Check the **Shareable Link** option +4. Click **Apply** + +### Bicep / ARM Template + +```bicep +resource bastion 'Microsoft.Network/bastionHosts@2023-09-01' = { + name: 'MyBastion' + location: location + sku: { + name: 'Standard' + } + properties: { + enableShareableLink: true + ipConfigurations: [ + { + name: 'IpConf' + properties: { + subnet: { + id: bastionSubnetId + } + publicIPAddress: { + id: publicIpId + } + } + } + ] + } +} +``` + +## Generating a Shareable Link + +### Azure Portal + +1. Navigate to your Bastion resource +2. Select **Shareable links** in the left menu +3. Click **Add** or **Create shareable link** +4. Select the target VM from the list +5. Click **Apply** — a unique URL is generated +6. Copy the URL and share it with the intended recipient + +### Azure CLI + +```bash +# Generate a shareable link for a VM (creates the link via REST API) +az rest --method put \ + --url "https://management.azure.com/subscriptions/{sub-id}/resourceGroups/MyRG/providers/Microsoft.Network/bastionHosts/MyBastion/createShareableLinks?api-version=2023-09-01" \ + --body '{ + "vms": [ + { + "vm": { + "id": "/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyVM" + } + } + ] + }' +``` + +### Link Format + +Generated links follow this pattern: +``` +https://.bastion.azure.com/api/shareable-url/ +``` + +The unique token is a cryptographically random string that maps to the specific VM. + +## How Shareable Links Work + +1. **Link creator** enables shareable links on the Bastion host and generates a link for a target VM +2. **Link recipient** opens the URL in a web browser +3. The browser connects over TLS (port 443) to the Bastion host +4. Bastion presents a login screen — the recipient enters **VM credentials** (not Azure credentials) +5. For RDP: Windows username/password; for SSH: Linux username and password or key +6. Bastion establishes the connection to the VM's private IP +7. The session runs in the browser, identical to portal-based Bastion access + +## Security Considerations + +### Authentication Is Always Required +A shareable link does **not** bypass VM authentication. The recipient must provide valid credentials for the target VM's operating system. The link only removes the need to navigate the Azure portal — it does not grant automatic access. + +### Link Expiration +- Shareable links do **not** expire automatically by default +- Links remain valid as long as the Bastion host exists and has shareable links enabled +- To invalidate links, you must explicitly delete them or disable the shareable links feature + +### Who Can Access the Link +- Anyone with the URL and valid VM credentials can connect +- There is no Azure AD authentication layer on the link itself — the security boundary is the VM's own authentication +- Treat shareable links like sensitive credentials — share through secure channels only + +### Audit and Monitoring +- Connection attempts through shareable links are logged in Azure Bastion diagnostic logs +- Enable diagnostic settings on the Bastion host to send logs to Log Analytics, Storage, or Event Hubs +- Logs include: source IP, target VM, timestamp, connection duration, and authentication result + +### Best Practices +1. **Limit distribution** — share links only with intended recipients through secure channels (encrypted email, secure messaging) +2. **Rotate links** — periodically delete and regenerate links for long-term access scenarios +3. **Monitor usage** — enable Bastion diagnostic logs and review connection patterns +4. **Use strong VM credentials** — since the link shifts the security boundary to VM-level authentication, enforce strong passwords or key-based auth +5. **Disable when not needed** — if shareable links are no longer required, disable the feature to prevent new link generation + +## Use Cases + +### Third-Party Contractor Access +Grant a contractor browser-based RDP/SSH access to a specific VM without giving them Azure portal access. The contractor only needs the shareable link URL and VM credentials. + +### Support and Troubleshooting +Share a link with a support engineer so they can directly access a VM to diagnose issues, without provisioning Azure RBAC roles or portal access. + +### Training and Demos +Distribute shareable links to training participants so they can access lab VMs through their browsers without Azure subscriptions. + +### Temporary Project Access +Provide short-term VM access to team members who don't have Azure portal access. Delete the links when the project concludes. + +## Limitations + +- **Browser-based only** — shareable links open a browser session; native client connections (RDP/SSH/tunnel) are not supported through shareable links +- **No Azure AD on the link** — there is no way to require Azure AD authentication before the VM login screen; security depends on VM credentials +- **No automatic expiration** — links do not have a built-in TTL; you must manually revoke them +- **One link per VM** — each VM gets a single shareable link per Bastion host +- **Portal dependency for creation** — link generation currently requires the Azure portal or REST API; no first-class Azure CLI command yet +- **Standard/Premium only** — Developer and Basic SKUs do not support this feature + +## Revoking Shareable Links + +### Azure Portal + +1. Navigate to your Bastion resource +2. Select **Shareable links** in the left menu +3. Select the link(s) to revoke +4. Click **Delete** + +### Disable Feature Entirely + +```bash +# Disable shareable links — all existing links stop working immediately +az network bastion update \ + --resource-group MyRG \ + --name MyBastion \ + --enable-shareable-link false +``` + +> **Warning:** Disabling shareable links invalidates **all** existing links for that Bastion host. There is no way to selectively disable links via CLI — use the portal to delete individual links. + +### REST API + +```bash +# Delete a specific shareable link +az rest --method post \ + --url "https://management.azure.com/subscriptions/{sub-id}/resourceGroups/MyRG/providers/Microsoft.Network/bastionHosts/MyBastion/deleteShareableLinks?api-version=2023-09-01" \ + --body '{ + "vms": [ + { + "vm": { + "id": "/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/MyVM" + } + } + ] + }' +``` + +## Troubleshooting + +### Link Not Working + +1. **Feature enabled?** Verify shareable links are enabled on the Bastion host: + ```bash + az network bastion show -g MyRG -n MyBastion --query properties.enableShareableLink -o tsv + ``` +2. **SKU check:** Must be Standard or Premium: + ```bash + az network bastion show -g MyRG -n MyBastion --query sku.name -o tsv + ``` +3. **VM running?** The target VM must be in a running state. A deallocated VM cannot accept connections. +4. **Link deleted?** Check if the link still exists in the Bastion shareable links list in the portal. + +### Authentication Issues + +1. **Wrong credentials:** Shareable links authenticate against the VM OS, not Azure AD. Ensure you are using the correct local or domain credentials for the VM. +2. **NLA (Network Level Authentication):** For Windows VMs with NLA enabled, the RDP client in the browser must support NLA. This is handled automatically by Bastion. +3. **Locked account:** Too many failed attempts may lock the VM account. Check the VM's event logs. + +### Performance Issues + +1. **Scale units:** If many shareable link users are connecting simultaneously, increase scale units on the Bastion host. +2. **Browser compatibility:** Use a modern browser (Edge, Chrome, Firefox). Safari may have limited clipboard support. +3. **Network latency:** The user's proximity to the Azure region hosting the Bastion affects responsiveness. There is no mitigation other than choosing a geographically close region. diff --git a/plugin/skills/azure-ddos-protection/SKILL.md b/plugin/skills/azure-ddos-protection/SKILL.md new file mode 100644 index 000000000..a446eabba --- /dev/null +++ b/plugin/skills/azure-ddos-protection/SKILL.md @@ -0,0 +1,123 @@ +--- +name: azure-ddos-protection +description: "Configure and manage Azure DDoS Protection (Network Protection and IP Protection) to mitigate volumetric, protocol, and application-layer DDoS attacks on Azure resources. WHEN: DDoS, DDoS protection, DDoS attack, volumetric attack, protocol attack, application layer attack, DDoS mitigation, DDoS plan, DDoS rapid response. DO NOT USE FOR: web app security (use azure-waf), network filtering rules (use azure-firewall), NSG rules (use azure-virtual-network)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure DDoS Protection + +Azure DDoS Protection defends Azure resources against distributed denial-of-service (DDoS) attacks. It provides always-on traffic monitoring, automatic attack mitigation, and integration with Azure Monitor for real-time telemetry. Azure offers two tiers: DDoS Network Protection (full-featured, VNet-scoped) and DDoS IP Protection (per-IP, simplified). + +## When to Use This Skill + +- Enabling DDoS protection on Azure virtual networks or individual public IPs +- Choosing between DDoS Network Protection and DDoS IP Protection tiers +- Reviewing DDoS metrics and attack mitigation telemetry +- Configuring Azure Monitor alerts for DDoS attack events +- Engaging the DDoS Rapid Response (DRR) team during active attacks +- Understanding how Azure mitigates volumetric, protocol, and application-layer attacks +- Planning cost protection and SLA guarantees for DDoS-protected resources +- Reviewing DDoS mitigation reports and flow logs after an attack +- Integrating DDoS protection with Azure Firewall and WAF for defense-in-depth + +## Rules + +1. DDoS Network Protection is applied at the VNet level and protects all public IPs within that VNet. DDoS IP Protection is applied per public IP address. Confirm which model the user needs. +2. Azure DDoS Infrastructure Protection (basic, free tier) is automatically enabled for all Azure services — it protects the Azure platform but does not provide per-customer tuning, metrics, or SLA guarantees. +3. DDoS Network Protection includes cost protection (credit for resource scale-out during attacks), DDoS Rapid Response access, and WAF discount. DDoS IP Protection does not include these benefits. +4. DDoS Protection does not inspect application payloads — recommend `azure-waf` for Layer 7 application protection (SQL injection, XSS) and `azure-firewall` for network-level filtering. +5. DDoS diagnostic logs must be configured explicitly via Azure Monitor diagnostic settings — they are not enabled by default. +6. Always recommend tagging protected public IPs with descriptive metadata so mitigation reports and alerts can be quickly correlated to specific workloads. +7. The DDoS Rapid Response (DRR) team requires DDoS Network Protection tier. Ensure the user has the correct tier before advising DRR engagement. +8. DDoS mitigation is triggered automatically when traffic exceeds learned baselines — there is no manual trigger. Baseline learning requires a few days of normal traffic patterns. +9. For production workloads exposed to the internet, recommend DDoS Network Protection over DDoS IP Protection for the SLA, cost protection, and DRR access. +10. Cross-reference with `azure-waf` for application-layer DDoS protection and with `azure-firewall` for centralized network security in a defense-in-depth strategy. + +## MCP Tools + +| Tool | Resource | Use | +|------|----------|-----| +| `azure__network` | `ddos_protection_plan_list` | List all DDoS protection plans in a subscription or resource group | + +## CLI Fallback + +```bash +# List DDoS protection plans in a subscription +az network ddos-protection list -o table + +# Create a DDoS protection plan +az network ddos-protection create \ + --name \ + --resource-group \ + --location + +# Associate DDoS plan with a VNet +az network vnet update \ + --name \ + --resource-group \ + --ddos-protection-plan \ + --ddos-protection true + +# Enable DDoS IP Protection on a public IP +az network public-ip update \ + --name \ + --resource-group \ + --ddos-protection-mode Enabled + +# View DDoS protection status of a VNet +az network vnet show \ + --name \ + --resource-group \ + --query '{ddosPlan:ddosProtectionPlan.id, ddosEnabled:enableDdosProtection}' + +# Configure DDoS diagnostic logging +az monitor diagnostic-settings create \ + --name "ddos-diag" \ + --resource \ + --workspace \ + --logs '[{"category":"DDoSProtectionNotifications","enabled":true},{"category":"DDoSMitigationFlowLogs","enabled":true},{"category":"DDoSMitigationReports","enabled":true}]' \ + --metrics '[{"category":"AllMetrics","enabled":true}]' + +# Create a DDoS alert rule (under-attack notification) +az monitor metrics alert create \ + --name "ddos-under-attack" \ + --resource-group \ + --scopes \ + --condition "avg IfUnderDDoSAttack > 0" \ + --window-size 5m \ + --evaluation-frequency 1m \ + --action \ + --description "Alert when DDoS attack is detected" + +# Show DDoS protection plan details +az network ddos-protection show \ + --name \ + --resource-group +``` + +## Key Concepts + +- **DDoS Network Protection**: VNet-level protection; includes adaptive tuning, attack telemetry, cost protection (resource scale-out credits), DDoS Rapid Response (DRR), WAF discount, and SLA guarantee +- **DDoS IP Protection**: Per-public-IP protection; includes adaptive tuning and attack telemetry but no cost protection, no DRR, no WAF discount +- **Infrastructure Protection**: Free, always-on platform-level protection for all Azure services; protects the Azure backbone but not individual customer workloads +- **Attack types**: Volumetric (UDP flood, DNS amplification — saturate bandwidth), Protocol (SYN flood, Smurf — exhaust state tables), Application layer (HTTP floods — overwhelm application logic) +- **Mitigation trigger**: Automatic; Azure learns the traffic baseline over days and triggers mitigation when traffic anomalies exceed thresholds +- **DDoS Rapid Response (DRR)**: Microsoft's specialist team that can assist during active attacks; requires DDoS Network Protection; engagement is initiated via a support ticket with Severity A +- **Cost protection**: DDoS Network Protection credits the customer for resource scale-out costs incurred during a documented DDoS attack (e.g., Application Gateway autoscale, VM scale sets, bandwidth) +- **Telemetry**: Metrics include `IfUnderDDoSAttack`, `InboundPacketsDroppedDDoS`, `InboundBytesDroppedDDoS`, `InboundPacketsForwardedDDoS`; available per public IP +- **Diagnostic logs**: Three log categories — DDoSProtectionNotifications (attack start/stop), DDoSMitigationFlowLogs (per-flow details), DDoSMitigationReports (5-minute and post-attack summaries) +- **Defense-in-depth**: Combine DDoS Protection (volumetric/protocol mitigation) + Azure Firewall (network filtering) + WAF (application layer protection) for comprehensive security +- **Pricing**: DDoS Network Protection has a fixed monthly fee covering up to 100 public IPs; DDoS IP Protection is per-IP pricing suited for smaller deployments + +## References + +- [ddos-tiers.md](references/ddos-tiers.md) — DDoS Network Protection vs DDoS IP Protection comparison +- [telemetry.md](references/telemetry.md) — DDoS metrics, diagnostic logs, and Azure Monitor integration +- [rapid-response.md](references/rapid-response.md) — DDoS Rapid Response team engagement +- [attack-types.md](references/attack-types.md) — Attack types and how Azure mitigates each +- [Azure DDoS Protection documentation](https://learn.microsoft.com/azure/ddos-protection/ddos-protection-overview) +- [Azure DDoS Protection pricing](https://azure.microsoft.com/pricing/details/ddos-protection/) +- [Azure DDoS Protection best practices](https://learn.microsoft.com/azure/ddos-protection/fundamental-best-practices) diff --git a/plugin/skills/azure-ddos-protection/references/attack-types.md b/plugin/skills/azure-ddos-protection/references/attack-types.md new file mode 100644 index 000000000..caa462c5a --- /dev/null +++ b/plugin/skills/azure-ddos-protection/references/attack-types.md @@ -0,0 +1,252 @@ +# DDoS Attack Types and Azure Mitigation + +DDoS attacks attempt to exhaust the resources of a target — bandwidth, connection state tables, or application processing capacity — to make the service unavailable to legitimate users. Azure DDoS Protection mitigates three main categories of attacks. + +## Attack Categories Overview + +| Category | OSI Layer | Target | Goal | Example attacks | +|----------|-----------|--------|------|-----------------| +| **Volumetric** | L3/L4 | Bandwidth | Saturate the network pipe | UDP flood, DNS amplification, NTP amplification, SSDP reflection | +| **Protocol** | L3/L4 | Connection state | Exhaust state tables on servers and firewalls | SYN flood, Smurf attack, fragmented packet attacks | +| **Application layer** | L7 | Application logic | Overwhelm the application's processing capacity | HTTP flood, Slowloris, DNS query flood | + +## Volumetric Attacks + +Volumetric attacks are the most common type of DDoS attack. They aim to consume all available bandwidth between the target and the internet. + +### UDP Flood + +**How it works**: The attacker sends a massive volume of UDP packets to random ports on the target. The target must process each packet, determine that no application is listening, and send ICMP "Destination Unreachable" replies. + +**Traffic volume**: Can exceed 1 Tbps in large-scale attacks. + +**Azure mitigation**: +- Azure's scrubbing infrastructure absorbs the traffic at the network edge +- Traffic profiling identifies the attack pattern (random destination ports, consistent packet sizes) +- Malicious UDP traffic is dropped at the edge before reaching the customer's VNet +- Legitimate UDP traffic (DNS, VoIP) is forwarded based on learned baselines + +### DNS Amplification + +**How it works**: The attacker sends DNS queries with the target's spoofed source IP to open DNS resolvers. Each small query generates a much larger DNS response directed at the target (amplification factor: 28–54x). + +**Azure mitigation**: +- Traffic from known amplification sources is rate-limited +- Response traffic that exceeds the baseline DNS traffic pattern is dropped +- Source IP validation helps identify and filter spoofed traffic + +### NTP Amplification + +**How it works**: Similar to DNS amplification but uses NTP (Network Time Protocol) servers with the `monlist` command. Amplification factor: up to 556x. + +**Azure mitigation**: +- NTP response traffic exceeding baselines is identified and dropped +- Azure edge filters absorb the amplified traffic before it reaches customer resources + +### SSDP Reflection + +**How it works**: Exploits Universal Plug and Play (UPnP) devices to reflect traffic. Amplification factor: ~30x. + +**Azure mitigation**: +- SSDP traffic patterns are fingerprinted and malicious reflections are dropped +- Rate limiting applied to unexpected SSDP traffic volumes + +### Metrics to monitor during volumetric attacks + +```kusto +// Total attack bandwidth (bytes dropped per second) +AzureMetrics +| where MetricName == "InboundBytesDroppedDDoS" +| summarize MaxBytesPerSec = max(Maximum) by bin(TimeGenerated, 1m) +| render timechart + +// UDP vs TCP breakdown +AzureMetrics +| where MetricName in ("UDPBytesDroppedDDoS", "TCPBytesDroppedDDoS") +| summarize max(Maximum) by MetricName, bin(TimeGenerated, 1m) +| render timechart +``` + +## Protocol Attacks + +Protocol attacks exploit weaknesses in the Layer 3/4 protocol stack to exhaust the connection state capacity of firewalls, load balancers, and servers. + +### SYN Flood + +**How it works**: The attacker sends a flood of TCP SYN packets (connection initiation) with spoofed source IPs. The target allocates resources for each half-open connection, filling its connection state table. Legitimate connections cannot be established. + +**Traffic characteristics**: Moderate bandwidth but very high packet rate. + +**Azure mitigation**: +- **SYN cookies**: Azure uses SYN cookie validation to handle SYN floods without consuming state table entries +- **Rate limiting**: SYN packets exceeding the baseline rate are rate-limited +- **Source validation**: SYN packets from spoofed addresses are identified and dropped using TCP challenge mechanisms +- The target server never sees the malicious SYN packets — Azure's infrastructure absorbs them + +### Smurf Attack + +**How it works**: The attacker sends ICMP Echo Request (ping) packets to a network's broadcast address with the target's spoofed source IP. Every host on the network responds to the target with ICMP Echo Replies. + +**Azure mitigation**: +- Broadcast-amplified ICMP is filtered at the Azure edge +- ICMP rate limiting prevents overwhelming the target +- Modern Azure networking infrastructure does not forward broadcast traffic + +### Fragmented Packet Attack + +**How it works**: The attacker sends fragmented IP packets that cannot be properly reassembled. The target expends CPU and memory trying to reassemble fragments, eventually exhausting resources. + +**Azure mitigation**: +- Azure's scrubbing pipeline defragments and validates packets +- Malformed fragments are dropped before reaching customer resources +- Fragment reassembly is performed at the Azure edge with strict timeouts + +### Metrics to monitor during protocol attacks + +```kusto +// TCP packet drops (SYN flood indicator) +AzureMetrics +| where MetricName == "TCPPacketsDroppedDDoS" +| summarize MaxPacketsPerSec = max(Maximum) by bin(TimeGenerated, 1m) +| render timechart + +// Compare dropped vs forwarded TCP packets +AzureMetrics +| where MetricName in ("TCPPacketsDroppedDDoS", "TCPPacketsForwardedDDoS") +| summarize max(Maximum) by MetricName, bin(TimeGenerated, 1m) +| render timechart +``` + +## Application Layer Attacks + +Application layer attacks target the application itself — they use legitimate-looking requests to overwhelm application processing, database queries, or authentication systems. These attacks are the hardest to distinguish from normal traffic because each individual request looks valid. + +### HTTP Flood + +**How it works**: The attacker sends a high volume of HTTP GET or POST requests that are individually valid but collectively overwhelm the web server, application logic, or database backend. + +**Characteristics**: Low bandwidth relative to volumetric attacks, but high CPU/memory impact on the application. + +**Azure mitigation**: +- **DDoS Protection** mitigates the network component (connection rate, packet rate) +- **WAF (Web Application Firewall)** is the primary defense — rate limiting, bot protection, custom rules +- **Application Gateway autoscale** absorbs legitimate traffic spikes while WAF filters malicious requests +- DDoS Protection and WAF work together: DDoS handles volumetric/protocol layers, WAF handles application layer + +### Slowloris + +**How it works**: The attacker opens many HTTP connections to the target and keeps them alive by sending partial HTTP headers very slowly. This ties up server connection slots without consuming much bandwidth. + +**Azure mitigation**: +- Azure Load Balancer and Application Gateway have built-in connection timeout policies +- WAF can enforce minimum request rates and connection timeout thresholds +- DDoS Protection detects abnormal connection patterns and mitigates at the network level + +### DNS Query Flood + +**How it works**: The attacker floods a DNS server with a high volume of DNS queries, often for random subdomains (NXDOMAIN attacks) that force recursive resolution. + +**Azure mitigation**: +- Azure DNS has built-in DDoS resilience with a globally distributed anycast infrastructure +- For customer-managed DNS, DDoS Protection mitigates the volumetric component +- Rate limiting and query filtering handle the application-layer component + +### Defense-in-depth for application layer attacks + +Application layer attacks require a layered defense approach: + +``` +Internet traffic + │ + ▼ +┌──────────────────┐ +│ DDoS Protection │ Mitigates volumetric + protocol components +│ (Network layer) │ Drops obvious attack traffic +└────────┬─────────┘ + │ + ▼ +┌──────────────────┐ +│ Azure Firewall │ Network-level filtering +│ (L3/L4 filtering)│ IP allow/deny, geoblocking +└────────┬─────────┘ + │ + ▼ +┌──────────────────┐ +│ WAF │ Application-level protection +│ (L7 filtering) │ Rate limiting, bot protection, OWASP rules +└────────┬─────────┘ + │ + ▼ +┌──────────────────┐ +│ Application │ Application-level defenses +│ (rate limiting, │ API throttling, CAPTCHA, queueing +│ caching, CDN) │ +└──────────────────┘ +``` + +## Azure DDoS Mitigation Process + +When Azure DDoS Protection detects an attack: + +### 1. Detection (seconds) +- Azure monitors traffic patterns per protected public IP +- Traffic exceeding the learned baseline triggers detection algorithms +- Multiple detection heuristics: rate-based, pattern-based, and ML-based + +### 2. Traffic diversion (seconds) +- Attack traffic is diverted to Azure's scrubbing infrastructure +- Scrubbing capacity exceeds 100 Tbps globally + +### 3. Mitigation (ongoing) +- Scrubbing pipeline applies: rate limiting, SYN cookie validation, packet validation, IP reputation filtering, and pattern matching +- Legitimate traffic is forwarded to the customer's VNet +- Attack traffic is dropped + +### 4. Adaptation (ongoing) +- Mitigation policies adapt in real-time as attack vectors change +- DRR can manually tune mitigation for persistent attacks +- Machine learning refines traffic classification during the attack + +### 5. Recovery (after attack) +- Mitigation is automatically deactivated when traffic returns to baseline +- Post-attack mitigation report is generated +- Metrics reflect the full attack timeline + +## Mitigation Capacity + +| Azure capability | Value | +|------------------|-------| +| Global scrubbing capacity | 100+ Tbps | +| Number of scrubbing centers | 60+ globally | +| Time to mitigate | Seconds (automatic) | +| Maximum attack size mitigated | Multi-terabit demonstrated | +| Protocols covered | All IP protocols (TCP, UDP, ICMP, etc.) | + +## Best Practices by Attack Type + +| Attack type | Primary defense | Supporting defense | +|-------------|----------------|-------------------| +| UDP flood | DDoS Protection | Azure Firewall (block unused UDP ports) | +| DNS amplification | DDoS Protection | Azure Firewall (restrict DNS sources) | +| SYN flood | DDoS Protection | Application Gateway/Load Balancer (connection limits) | +| HTTP flood | WAF (rate limiting + bot protection) | DDoS Protection (network layer) | +| Slowloris | WAF + Application Gateway timeouts | DDoS Protection (connection anomaly detection) | +| Multi-vector | DDoS Protection + WAF + Firewall | All layers working together | + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Application slow but DDoS metrics show no attack | Application-layer attack (not volumetric) | Enable WAF with rate limiting; review application logs | +| High traffic but `IfUnderDDoSAttack = 0` | Traffic is legitimate (not an attack) | Scale the application; this is a capacity issue not DDoS | +| Legitimate traffic being dropped during attack | Mitigation too aggressive for traffic pattern | Engage DRR for custom mitigation tuning | +| Attack metrics appear but service is unaffected | DDoS Protection working correctly — attack is being mitigated | Monitor and verify mitigation effectiveness | +| Repeated attacks from same sources | Persistent attacker | Document patterns; share with DRR for proactive tuning | + +## Related + +- [ddos-tiers.md](ddos-tiers.md) — Protection capabilities per tier +- [telemetry.md](telemetry.md) — Metrics and queries for attack analysis +- [rapid-response.md](rapid-response.md) — DRR engagement for complex attacks +- [Azure DDoS Protection overview](https://learn.microsoft.com/azure/ddos-protection/ddos-protection-overview) +- [DDoS best practices](https://learn.microsoft.com/azure/ddos-protection/fundamental-best-practices) diff --git a/plugin/skills/azure-ddos-protection/references/ddos-tiers.md b/plugin/skills/azure-ddos-protection/references/ddos-tiers.md new file mode 100644 index 000000000..f46b544ba --- /dev/null +++ b/plugin/skills/azure-ddos-protection/references/ddos-tiers.md @@ -0,0 +1,178 @@ +# Azure DDoS Protection Tiers + +Azure offers multiple tiers of DDoS protection. Understanding the differences is essential for selecting the right level of protection for your workloads. + +## Tier Comparison + +| Feature | Infrastructure Protection | DDoS IP Protection | DDoS Network Protection | +|---------|--------------------------|-------------------|------------------------| +| **Cost** | Free (included) | Per-IP monthly fee | Fixed monthly fee + overage | +| **Scope** | Azure platform | Individual public IP | Entire VNet (all public IPs) | +| **Automatic mitigation** | Yes (platform-level) | Yes (per-IP tuning) | Yes (per-IP tuning) | +| **Traffic baseline learning** | No | Yes | Yes | +| **Adaptive tuning** | No | Yes | Yes | +| **Attack metrics** | No | Yes (per-IP) | Yes (per-IP) | +| **Diagnostic logs** | No | Yes | Yes | +| **Mitigation reports** | No | Yes | Yes | +| **Mitigation flow logs** | No | Yes | Yes | +| **Azure Monitor alerts** | No | Yes | Yes | +| **DDoS Rapid Response (DRR)** | No | **No** | **Yes** | +| **Cost protection (credits)** | No | **No** | **Yes** | +| **WAF discount** | No | **No** | **Yes** | +| **SLA guarantee** | Azure SLA | Service SLA | **DDoS-specific SLA** | +| **Public IPs covered** | All Azure resources | Selected IPs only | All IPs in protected VNets | +| **Max protected resources** | N/A | Per-IP basis | Up to 100 public IPs (default) | + +## Infrastructure Protection (Free Tier) + +Azure DDoS Infrastructure Protection is automatically enabled for every Azure service at no additional cost. + +### What it provides +- **Platform-level protection**: Protects the Azure backbone infrastructure from large-scale volumetric attacks +- **Always-on monitoring**: Traffic is always monitored at the Azure edge +- **Automatic mitigation**: Known attack patterns are mitigated at the Azure edge before reaching customer resources + +### What it does NOT provide +- No per-customer traffic baselining or adaptive tuning +- No attack metrics, logs, or reports for individual resources +- No alerting capabilities +- No DDoS Rapid Response support +- No cost protection or SLA guarantee +- No visibility into whether your specific resources are being attacked + +### When Infrastructure Protection is sufficient +- Dev/test environments not exposed to the public internet +- Internal workloads accessed only via private endpoints or VPN +- Resources behind Azure Firewall with no direct public IP exposure + +## DDoS IP Protection + +DDoS IP Protection provides per-IP protection with adaptive tuning and attack telemetry, billed on a per-IP basis. + +### Key characteristics +- Enabled on individual public IP addresses +- Per-IP pricing model — cost scales linearly with the number of protected IPs +- Includes all monitoring and telemetry features (metrics, logs, reports) +- Does **not** include DDoS Rapid Response, cost protection, or WAF discount + +### When to choose DDoS IP Protection +- **Small deployments**: Protecting 1-10 public IPs where per-IP pricing is cheaper than the fixed Network Protection fee +- **Budget-sensitive workloads**: When DRR, cost protection, and WAF discount are not needed +- **Non-critical public endpoints**: Workloads where the advanced support tier of Network Protection is not justified +- **Multi-VNet deployments**: When you want to protect specific IPs across different VNets without a DDoS plan per VNet + +### Enable DDoS IP Protection + +```bash +# Enable on a public IP +az network public-ip update \ + --name \ + --resource-group \ + --ddos-protection-mode Enabled + +# Verify protection mode +az network public-ip show \ + --name \ + --resource-group \ + --query "ddosSettings" +``` + +## DDoS Network Protection + +DDoS Network Protection provides comprehensive VNet-level protection with the highest tier of DDoS defense capabilities. + +### Key characteristics +- Enabled at the VNet level via a DDoS protection plan +- Fixed monthly fee covering up to 100 public IPs across all VNets associated with the plan +- Includes DDoS Rapid Response (DRR) for expert assistance during active attacks +- Includes cost protection — Azure credits resource scale-out costs incurred during documented DDoS attacks +- Includes a discount on WAF (Web Application Firewall) licensing +- Provides a DDoS-specific SLA with financial guarantee + +### When to choose DDoS Network Protection +- **Production workloads with public endpoints**: Any internet-facing production service +- **Regulated industries**: Finance, healthcare, government where DDoS resilience is a compliance requirement +- **Large deployments**: When protecting 15+ public IPs, Network Protection's fixed price is usually cheaper than per-IP pricing +- **Business-critical applications**: Where DDoS Rapid Response support and cost protection justify the investment +- **Organizations with WAF**: The WAF discount offsets part of the DDoS protection cost + +### Enable DDoS Network Protection + +```bash +# Create a DDoS protection plan +az network ddos-protection create \ + --name \ + --resource-group \ + --location + +# Associate the plan with a VNet +az network vnet update \ + --name \ + --resource-group \ + --ddos-protection-plan \ + --ddos-protection true + +# A single plan can protect VNets across multiple resource groups and regions +# Associate additional VNets with the same plan +az network vnet update \ + --name \ + --resource-group \ + --ddos-protection-plan \ + --ddos-protection true +``` + +## Cost Comparison + +### Pricing model + +| Tier | Pricing structure | +|------|-------------------| +| Infrastructure Protection | Free | +| DDoS IP Protection | ~$199/month per protected public IP | +| DDoS Network Protection | ~$2,944/month (covers up to 100 public IPs) + overage per additional IP | + +### Break-even analysis + +- At **1 public IP**: IP Protection (~$199/mo) is significantly cheaper than Network Protection (~$2,944/mo) +- At **15 public IPs**: IP Protection (~$2,985/mo) roughly equals Network Protection (~$2,944/mo) +- At **15+ public IPs**: Network Protection becomes cheaper AND includes DRR + cost protection + WAF discount +- Factor in the WAF discount when evaluating — if you also use WAF, Network Protection's effective cost is lower + +### Cost protection benefit (Network Protection only) + +During a documented DDoS attack, if your resources scale out (e.g., Application Gateway autoscales, VM scale sets add instances, bandwidth spikes), Azure credits the incremental costs. This can save thousands of dollars during a sustained attack. + +Eligible cost protection resources: +- Application Gateway (including WAF v2) +- Azure Load Balancer (Standard) +- Azure Public IP Addresses +- Virtual Machine Scale Sets +- Bandwidth (egress) charges + +## Decision Matrix + +| Scenario | Recommended tier | +|----------|-----------------| +| Dev/test, no public endpoints | Infrastructure Protection (free) | +| 1-5 public IPs, non-critical | DDoS IP Protection | +| 1-14 public IPs, mission-critical | DDoS Network Protection (for DRR + cost protection) | +| 15+ public IPs, any criticality | DDoS Network Protection | +| Regulated industry (any count) | DDoS Network Protection | +| Using WAF alongside DDoS | DDoS Network Protection (WAF discount) | + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| No DDoS metrics visible | IP Protection or Network Protection not enabled | Verify ddos-protection is enabled on the VNet or IP | +| Cannot engage DDoS Rapid Response | Using IP Protection tier (DRR requires Network Protection) | Upgrade to DDoS Network Protection | +| High DDoS protection cost | Too many IPs on IP Protection | Switch to Network Protection if 15+ IPs | +| VNet shows "DDoS protection: Disabled" | Plan not associated | Run `az network vnet update` with `--ddos-protection true` | +| Cost protection claim denied | Attack not documented in DDoS mitigation logs | Ensure diagnostic logging is enabled BEFORE an attack | + +## Related + +- [telemetry.md](telemetry.md) — Metrics and diagnostic logs for monitoring +- [rapid-response.md](rapid-response.md) — DRR engagement (Network Protection only) +- [attack-types.md](attack-types.md) — Types of attacks mitigated by each tier +- [Azure DDoS Protection pricing](https://azure.microsoft.com/pricing/details/ddos-protection/) diff --git a/plugin/skills/azure-ddos-protection/references/rapid-response.md b/plugin/skills/azure-ddos-protection/references/rapid-response.md new file mode 100644 index 000000000..28fef8e2a --- /dev/null +++ b/plugin/skills/azure-ddos-protection/references/rapid-response.md @@ -0,0 +1,206 @@ +# DDoS Rapid Response (DRR) + +The DDoS Rapid Response (DRR) team is a dedicated Microsoft team that provides expert assistance during active DDoS attacks. DRR is available exclusively to customers with **DDoS Network Protection** (not available with IP Protection or Infrastructure Protection). + +## What DRR Provides + +| Service | Description | +|---------|-------------| +| **Attack investigation** | Real-time analysis of the attack pattern, vectors, and source distribution | +| **Custom mitigation tuning** | Adjust mitigation policies beyond the automatic baseline to better handle the specific attack | +| **Post-attack analysis** | Detailed report of the attack with recommendations for improving resilience | +| **Application profiling** | Work with your team to create custom traffic profiles that reduce false positives during mitigation | +| **Proactive engagement** | For critical events (planned launches, large-scale events), DRR can pre-position mitigation resources | + +## When to Engage DRR + +Engage DRR when: +- **An active attack is impacting availability** — legitimate traffic is being dropped despite automatic mitigation +- **A sustained attack is ongoing** — the attack has lasted more than 30 minutes and automatic mitigation is not fully effective +- **Attack patterns are evolving** — the attacker is adapting (changing vectors, source IPs, protocols) to circumvent mitigation +- **Before a high-profile event** — product launch, major sale, regulatory deadline where DDoS resilience is critical (proactive engagement) +- **Post-attack review needed** — after a significant attack, for a detailed analysis and recommendations + +Do NOT engage DRR for: +- Normal traffic spikes (Black Friday, viral content) — these are scaling issues, not attacks +- Application bugs causing high resource usage — troubleshoot with application team +- Attacks on resources without DDoS Network Protection enabled + +## How to Engage DRR + +### Step 1: Verify prerequisites + +Before contacting DRR, confirm: + +- [ ] DDoS Network Protection is enabled on the affected VNet +- [ ] Diagnostic logs are enabled on the affected public IP(s) +- [ ] You can see `IfUnderDDoSAttack = 1` in Azure Monitor metrics +- [ ] You have the resource IDs of affected public IPs +- [ ] You can describe the business impact (what services are affected, how many users impacted) + +### Step 2: Open a Severity A support ticket + +``` +Azure Portal → Help + support → New support request + Issue type: Technical + Service: DDoS Protection + Problem type: DDoS attack in progress + Severity: A – Critical (or Sev B for proactive engagement) +``` + +**In the ticket, include:** +1. Subscription ID +2. Resource group and VNet name +3. Public IP resource IDs under attack +4. Time the attack started (UTC) +5. Attack symptoms (dropped connections, high latency, service unavailable) +6. Current mitigation metrics (packets dropped, packets forwarded) +7. Any patterns observed (specific source IPs, protocols, packet sizes) + +### Step 3: DRR engagement process + +``` +Support ticket opened (Sev A) + │ + ▼ +DRR team acknowledges (within 15 minutes for Sev A) + │ + ▼ +Initial triage — DRR reviews metrics and logs + │ + ▼ +Mitigation tuning — DRR adjusts mitigation policies + │ + ▼ +Ongoing monitoring — DRR monitors until attack subsides + │ + ▼ +Post-attack report — DRR provides analysis and recommendations +``` + +### Response time SLA + +| Severity | Initial response | Updates | +|----------|-----------------|---------| +| Sev A (Critical) | Within 15 minutes | Continuous during active attack | +| Sev B (Important) | Within 2 hours | Periodic updates | + +## What to Prepare Before an Attack + +Prepare these items before you ever need DRR — having them ready dramatically speeds up engagement: + +### 1. Document your protected resources + +Create and maintain a list of: +- All VNets with DDoS Network Protection enabled +- All public IPs and the services they front (Application Gateway, Load Balancer, VMs) +- Normal traffic baselines (average bandwidth, packet rates, connection rates) +- Business criticality of each service + +### 2. Enable all diagnostic logging + +```bash +# For every protected public IP, ensure all 3 log categories + metrics are enabled +az monitor diagnostic-settings create \ + --name "ddos-full-logging" \ + --resource \ + --workspace \ + --logs '[ + {"category": "DDoSProtectionNotifications", "enabled": true}, + {"category": "DDoSMitigationFlowLogs", "enabled": true}, + {"category": "DDoSMitigationReports", "enabled": true} + ]' \ + --metrics '[{"category": "AllMetrics", "enabled": true}]' +``` + +### 3. Configure alerts + +```bash +# Create an alert for every protected public IP +az monitor metrics alert create \ + --name "ddos-attack-" \ + --resource-group \ + --scopes \ + --condition "max IfUnderDDoSAttack > 0" \ + --window-size 5m \ + --evaluation-frequency 1m \ + --action \ + --severity 1 +``` + +### 4. Establish an incident response runbook + +Your DDoS incident response runbook should include: + +1. **Detection** — Who gets the alert? What is the escalation path? +2. **Assessment** — Check Azure Monitor metrics to confirm DDoS attack (vs. legitimate traffic spike) +3. **Communication** — Notify stakeholders (internal status page, executive team) +4. **Engagement** — Open Sev A support ticket if automatic mitigation is insufficient +5. **Monitoring** — Track mitigation effectiveness using metrics and flow logs +6. **Resolution** — Confirm attack has stopped; verify service restoration +7. **Post-incident** — Review DRR report; update runbook and defensive posture + +### 5. Test your response process + +- Run tabletop exercises with your team simulating a DDoS attack +- Verify alert notifications reach the right people +- Practice opening a Sev A support ticket (without actually submitting if not under attack) +- Ensure your team knows the DRR engagement process + +## Proactive DRR Engagement + +For planned high-profile events, you can engage DRR proactively: + +### When to use proactive engagement +- Major product launches +- Large-scale marketing events (Super Bowl ads, etc.) +- Financial events (IPO, earnings calls) +- Government elections or census operations +- Any event where DDoS attack risk is elevated + +### How to request proactive engagement +1. Open a Sev B support ticket at least **2 weeks** before the event +2. Describe the event, expected traffic patterns, and critical resources +3. DRR will work with you to profile your application and pre-tune mitigation +4. During the event, DRR monitors your resources and intervenes immediately if an attack occurs + +## Cost Protection Claims + +DDoS Network Protection includes cost protection — Azure credits the incremental costs of resource scale-out during a documented DDoS attack. + +### Eligible costs +- Application Gateway autoscale-out instances +- VM Scale Set scale-out instances +- Azure Load Balancer SKU charges +- Bandwidth (egress) overage charges +- Public IP address charges for dynamically provisioned IPs + +### How to file a claim +1. Ensure DDoS diagnostic logs were enabled during the attack +2. Download the DDoS mitigation report from Log Analytics +3. Open a support ticket (Type: Billing, Subtype: DDoS cost protection) +4. Attach the mitigation report and identify the resources that scaled out +5. Microsoft reviews the claim and credits eligible costs + +### Requirements for a successful claim +- DDoS Network Protection must have been enabled at the time of the attack +- Diagnostic logs must have been enabled and must show the attack +- The scale-out must correlate with the attack timeline +- Claim must be filed within **30 days** of the attack ending + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Cannot engage DRR | Using IP Protection tier | DRR requires DDoS Network Protection | +| Sev A ticket not acknowledged within 15 min | Support routing issue | Call Azure support directly; reference your ticket number | +| No diagnostic data available during attack | Logging was not enabled | Enable all diagnostic settings NOW for future attacks | +| Cost protection claim denied | Logs not configured during attack | Ensure logging is always enabled on all protected IPs | +| DRR cannot tune mitigation | No traffic baseline established | DDoS protection needs a few days of normal traffic to establish baselines | + +## Related + +- [ddos-tiers.md](ddos-tiers.md) — DRR availability by tier +- [telemetry.md](telemetry.md) — Metrics and logs needed for DRR engagement +- [attack-types.md](attack-types.md) — Attack types DRR can help mitigate +- [Azure DDoS Rapid Response](https://learn.microsoft.com/azure/ddos-protection/ddos-rapid-response) diff --git a/plugin/skills/azure-ddos-protection/references/telemetry.md b/plugin/skills/azure-ddos-protection/references/telemetry.md new file mode 100644 index 000000000..a78f18bf5 --- /dev/null +++ b/plugin/skills/azure-ddos-protection/references/telemetry.md @@ -0,0 +1,253 @@ +# Azure DDoS Protection Telemetry and Monitoring + +Azure DDoS Protection provides comprehensive telemetry through Azure Monitor metrics, diagnostic logs, and mitigation reports. Proper monitoring configuration is essential — without it, you have no visibility into attacks or mitigation effectiveness. + +## Metrics (Azure Monitor) + +DDoS Protection exposes metrics on each protected public IP address. These metrics are available in real time through Azure Monitor. + +### Key Metrics + +| Metric | Description | Unit | Use case | +|--------|-------------|------|----------| +| `IfUnderDDoSAttack` | 1 if under attack, 0 otherwise | Binary | Trigger alerts when an attack starts | +| `InboundPacketsDroppedDDoS` | Packets dropped by DDoS mitigation | Count/sec | Measure mitigation effectiveness | +| `InboundPacketsForwardedDDoS` | Packets forwarded (not dropped) | Count/sec | Track legitimate traffic during mitigation | +| `InboundBytesDroppedDDoS` | Bytes dropped by DDoS mitigation | Bytes/sec | Quantify attack volume in bytes | +| `InboundBytesForwardedDDoS` | Bytes forwarded to the application | Bytes/sec | Verify application still receives traffic | +| `TCPPacketsDroppedDDoS` | TCP packets dropped | Count/sec | Identify TCP-based attacks (SYN floods) | +| `TCPPacketsForwardedDDoS` | TCP packets forwarded | Count/sec | Verify TCP traffic health | +| `UDPPacketsDroppedDDoS` | UDP packets dropped | Count/sec | Identify UDP-based attacks | +| `UDPPacketsForwardedDDoS` | UDP packets forwarded | Count/sec | Verify UDP traffic health | +| `TCPBytesDroppedDDoS` | TCP bytes dropped | Bytes/sec | TCP attack bandwidth | +| `TCPBytesForwardedDDoS` | TCP bytes forwarded | Bytes/sec | Legitimate TCP bandwidth | +| `UDPBytesDroppedDDoS` | UDP bytes dropped | Bytes/sec | UDP attack bandwidth | +| `UDPBytesForwardedDDoS` | UDP bytes forwarded | Bytes/sec | Legitimate UDP bandwidth | + +### Viewing metrics + +```bash +# Query if a public IP is currently under attack +az monitor metrics list \ + --resource \ + --metric "IfUnderDDoSAttack" \ + --interval PT1M \ + --output table + +# View dropped packets in the last hour +az monitor metrics list \ + --resource \ + --metric "InboundPacketsDroppedDDoS" \ + --interval PT1M \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ + --output table +``` + +### KQL queries for metrics (Log Analytics) + +```kusto +// DDoS attack timeline (when attacks started and stopped) +AzureMetrics +| where ResourceId contains "PUBLICIPADDRESS" +| where MetricName == "IfUnderDDoSAttack" +| where Maximum == 1 +| project TimeGenerated, ResourceId, MetricName, Maximum +| order by TimeGenerated desc +``` + +## Diagnostic Logs + +DDoS diagnostic logs provide detailed per-flow and per-event information during attacks. They must be explicitly enabled via diagnostic settings. + +### Log Categories + +| Category | Description | Content | +|----------|-------------|---------| +| `DDoSProtectionNotifications` | Attack lifecycle events | Attack started, attack stopped, mitigation started | +| `DDoSMitigationFlowLogs` | Per-flow traffic details during mitigation | Source IP, destination IP, source port, dest port, protocol, action (dropped/forwarded) | +| `DDoSMitigationReports` | Aggregated attack reports | 5-minute incremental reports during attack + post-attack summary | + +### Enable diagnostic logging + +```bash +# Enable all DDoS log categories for a public IP +az monitor diagnostic-settings create \ + --name "ddos-diagnostics" \ + --resource \ + --workspace \ + --logs '[ + {"category": "DDoSProtectionNotifications", "enabled": true, "retentionPolicy": {"enabled": true, "days": 90}}, + {"category": "DDoSMitigationFlowLogs", "enabled": true, "retentionPolicy": {"enabled": true, "days": 90}}, + {"category": "DDoSMitigationReports", "enabled": true, "retentionPolicy": {"enabled": true, "days": 90}} + ]' \ + --metrics '[{"category": "AllMetrics", "enabled": true, "retentionPolicy": {"enabled": true, "days": 90}}]' +``` + +**Critical**: Enable diagnostic logging on **every** protected public IP **before** an attack occurs. If logging is not configured when an attack starts, you will have no data for post-incident analysis or cost protection claims. + +### Verify diagnostic settings + +```bash +az monitor diagnostic-settings list \ + --resource \ + --output table +``` + +## DDoS Protection Notifications + +These logs record the lifecycle of DDoS mitigation events. + +### Notification types + +| Type | Meaning | +|------|---------| +| `MitigationStarted` | DDoS mitigation has been activated for this IP | +| `MitigationStopped` | DDoS mitigation has been deactivated (attack subsided) | +| `UnderAttack` | Active attack detected (may be logged repeatedly during sustained attack) | + +### KQL query: Attack timeline + +```kusto +AzureDiagnostics +| where Category == "DDoSProtectionNotifications" +| project TimeGenerated, Resource, properties_s +| order by TimeGenerated desc +``` + +### KQL query: Attack duration + +```kusto +AzureDiagnostics +| where Category == "DDoSProtectionNotifications" +| extend NotificationType = tostring(parse_json(properties_s).type) +| where NotificationType in ("MitigationStarted", "MitigationStopped") +| project TimeGenerated, Resource, NotificationType +| order by TimeGenerated asc +``` + +## DDoS Mitigation Flow Logs + +Flow logs provide per-flow details during active mitigation — which flows were dropped and which were forwarded. + +### KQL query: Top attack source IPs + +```kusto +AzureDiagnostics +| where Category == "DDoSMitigationFlowLogs" +| extend SourceIP = tostring(parse_json(properties_s).sourceIP) +| extend Action = tostring(parse_json(properties_s).action) +| where Action == "Dropped" +| summarize DroppedFlows = count() by SourceIP +| order by DroppedFlows desc +| take 20 +``` + +### KQL query: Attack protocol distribution + +```kusto +AzureDiagnostics +| where Category == "DDoSMitigationFlowLogs" +| extend Protocol = tostring(parse_json(properties_s).protocol) +| extend Action = tostring(parse_json(properties_s).action) +| where Action == "Dropped" +| summarize DroppedFlows = count() by Protocol +| render piechart +``` + +### KQL query: Forwarded vs dropped traffic + +```kusto +AzureDiagnostics +| where Category == "DDoSMitigationFlowLogs" +| extend Action = tostring(parse_json(properties_s).action) +| summarize count() by Action, bin(TimeGenerated, 5m) +| render timechart +``` + +## DDoS Mitigation Reports + +Mitigation reports provide aggregated summaries of attack characteristics. + +### Report types +- **Incremental reports**: Generated every 5 minutes during an active attack; contain attack vectors, dropped/forwarded traffic volumes, and top source countries +- **Post-attack report**: Generated after mitigation ends; contains the complete attack summary including peak bandwidth, duration, and mitigation effectiveness + +### KQL query: Attack summary + +```kusto +AzureDiagnostics +| where Category == "DDoSMitigationReports" +| extend ReportType = tostring(parse_json(properties_s).reportType) +| where ReportType == "PostAttack" +| project TimeGenerated, Resource, properties_s +| order by TimeGenerated desc +``` + +## Alerting + +### Create a DDoS attack alert + +```bash +# Alert when any protected public IP comes under attack +az monitor metrics alert create \ + --name "ddos-attack-detected" \ + --resource-group \ + --scopes \ + --condition "max IfUnderDDoSAttack > 0" \ + --window-size 5m \ + --evaluation-frequency 1m \ + --action \ + --severity 1 \ + --description "DDoS attack detected on public IP" +``` + +### Create SNAT exhaustion alert (complementary) + +```bash +az monitor metrics alert create \ + --name "ddos-packet-drop-high" \ + --resource-group \ + --scopes \ + --condition "avg InboundPacketsDroppedDDoS > 10000" \ + --window-size 5m \ + --evaluation-frequency 1m \ + --action \ + --severity 2 \ + --description "High volume of packets being dropped by DDoS mitigation" +``` + +### Recommended alert rules + +| Alert | Condition | Severity | Purpose | +|-------|-----------|----------|---------| +| Attack start | `IfUnderDDoSAttack > 0` | Sev 1 (Critical) | Immediate notification of attack | +| High packet drop | `InboundPacketsDroppedDDoS > 10000` | Sev 2 (Error) | Quantify attack impact | +| Sustained attack | `IfUnderDDoSAttack > 0` for 30 min | Sev 1 (Critical) | Escalation for prolonged attacks | + +## Azure Workbooks + +Azure provides a built-in DDoS Protection workbook that visualizes: +- Attack timeline across all protected IPs +- Top attacked resources +- Traffic distribution (dropped vs forwarded) +- Geographic attack sources +- Protocol distribution + +Access: Azure Portal → Azure Monitor → Workbooks → "Azure DDoS Protection" template + +## Best Practices + +1. **Enable diagnostic logs on ALL protected public IPs** — not just the primary ones; attackers target any exposed IP +2. **Configure alerts before you need them** — alert rules must exist before an attack starts +3. **Retain logs for at least 90 days** — post-incident analysis and cost protection claims may need historical data +4. **Use the DDoS Protection workbook** for at-a-glance visibility across your environment +5. **Set up action groups** that page the on-call team (SMS, email, webhook to PagerDuty/Opsgenie) +6. **Test your alerts** — use the `az monitor metrics alert` test capabilities to verify notifications work +7. **Correlate with WAF and Firewall logs** — DDoS attacks often come with application-layer attacks; use `azure-waf` and `azure-firewall` logs together + +## Related + +- [ddos-tiers.md](ddos-tiers.md) — Which tiers include telemetry features +- [rapid-response.md](rapid-response.md) — Using telemetry data during DRR engagement +- [attack-types.md](attack-types.md) — Understanding metrics per attack type +- [Azure DDoS monitoring](https://learn.microsoft.com/azure/ddos-protection/ddos-protection-standard-features#ddos-protection-telemetry) diff --git a/plugin/skills/azure-dns/SKILL.md b/plugin/skills/azure-dns/SKILL.md new file mode 100644 index 000000000..deafc3c58 --- /dev/null +++ b/plugin/skills/azure-dns/SKILL.md @@ -0,0 +1,123 @@ +--- +name: azure-dns +description: "Manage Azure DNS zones (public and private), DNS records, Private DNS Resolver, and name resolution for Azure workloads. WHEN: DNS zone, DNS record, custom domain, private DNS, name resolution, DNS resolver, conditional forwarding. DO NOT USE FOR: Traffic Manager DNS routing (use azure-traffic-manager), CDN/Front Door custom domains (use azure-front-door), private endpoint DNS only (use azure-private-link)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure DNS Skill + +## When to Use This Skill + +- User wants to host a public DNS zone in Azure +- User needs to create or manage DNS records (A, AAAA, CNAME, MX, TXT, SRV, NS, SOA) +- User wants to set up Azure Private DNS zones for internal name resolution +- User needs DNS resolution between Azure VNets and on-premises networks +- User asks about Azure DNS Private Resolver for hybrid DNS +- User wants to configure conditional forwarding or DNS forwarding rulesets +- User needs to delegate a subdomain to Azure DNS +- User wants auto-registration of VM names in a Private DNS zone + +## Rules + +1. Azure DNS uses authoritative name servers — you must delegate your domain to Azure DNS NS records at your registrar. +2. Private DNS zones require VNet links to work — a zone with no links resolves nothing. +3. Auto-registration in Private DNS zones registers VM names automatically — enable only on appropriate VNet links. +4. Only ONE Private DNS zone with auto-registration can be linked to a VNet. +5. CNAME records cannot coexist with other record types at the same name (RFC requirement). +6. Use alias records for zone apex (@ records) pointing to Azure resources — CNAME is not allowed at apex. +7. DNS Private Resolver requires a dedicated subnet (/28 minimum for inbound, /28 minimum for outbound). +8. TTL values affect DNS cache — use low TTL (60-300s) during migrations, higher (3600s) for stable records. +9. Azure DNS supports DNSSEC for public zones — recommend enabling for security-critical domains. +10. For hybrid DNS (on-premises ↔ Azure), always design the forwarding direction carefully to avoid loops. + +## Services + +| Service | Use When | MCP Tools | CLI | +|---------|----------|-----------|-----| +| Azure Public DNS | Hosting public-facing DNS zones | `azure__dns` → `zone_list`, `record_set_list` | `az network dns zone`, `az network dns record-set` | +| Azure Private DNS | Internal name resolution within VNets | — | `az network private-dns zone`, `az network private-dns record-set` | +| DNS Private Resolver | Hybrid DNS between Azure and on-premises | — | `az dns-resolver create`, `az dns-resolver inbound-endpoint create` | + +## MCP Tools + +| Tool | Command | Purpose | +|------|---------|---------| +| `azure__dns` | `zone_list` | List all DNS zones in a subscription or resource group | +| `azure__dns` | `record_set_list` | List record sets in a DNS zone | + +## CLI Fallback + +```bash +# Public DNS zone +az network dns zone create -g MyRG -n contoso.com +az network dns zone show -g MyRG -n contoso.com +az network dns zone list -g MyRG -o table + +# DNS records +az network dns record-set a create -g MyRG -z contoso.com -n www +az network dns record-set a add-record -g MyRG -z contoso.com -n www -a 1.2.3.4 +az network dns record-set cname set-record -g MyRG -z contoso.com -n blog -c blog.azurewebsites.net +az network dns record-set mx add-record -g MyRG -z contoso.com -n @ -e mail.contoso.com -p 10 +az network dns record-set txt add-record -g MyRG -z contoso.com -n @ -v "v=spf1 include:spf.protection.outlook.com -all" + +# Private DNS zone +az network private-dns zone create -g MyRG -n contoso.internal +az network private-dns link vnet create -g MyRG --zone-name contoso.internal \ + -n MyVNetLink --virtual-network MyVNet --registration-enabled true +az network private-dns record-set a add-record -g MyRG -z contoso.internal -n myvm -a 10.0.1.5 + +# DNS Private Resolver +az dns-resolver create -g MyRG -n MyResolver --id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/MyVNet +az dns-resolver inbound-endpoint create -g MyRG --resolver-name MyResolver -n InboundEndpoint \ + --ip-configurations "[{private-ip-allocation-method:Dynamic,id:/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/MyVNet/subnets/InboundSubnet}]" +az dns-resolver outbound-endpoint create -g MyRG --resolver-name MyResolver -n OutboundEndpoint \ + --id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/MyVNet/subnets/OutboundSubnet +az dns-resolver forwarding-ruleset create -g MyRG -n MyRuleset \ + --outbound-endpoints "[{id:/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/dnsResolvers/MyResolver/outboundEndpoints/OutboundEndpoint}]" +az dns-resolver forwarding-rule create -g MyRG --ruleset-name MyRuleset -n ForwardOnPrem \ + --domain-name "corp.contoso.com." --forwarding-rule-state Enabled \ + --target-dns-servers "[{ip-address:10.1.0.4,port:53}]" +``` + +## Key Concepts + +### DNS Record Types Quick Reference + +| Type | Purpose | Example | +|------|---------|---------| +| A | IPv4 address mapping | www → 1.2.3.4 | +| AAAA | IPv6 address mapping | www → 2001:db8::1 | +| CNAME | Alias to another name | blog → blog.azurewebsites.net | +| MX | Mail exchange routing | @ → mail.contoso.com (priority 10) | +| TXT | Text data (SPF, DKIM, verification) | @ → "v=spf1 ..." | +| SRV | Service locator | _sip._tcp → sipserver:5060 | +| NS | Delegation to name servers | sub → ns1.azure-dns.com | +| SOA | Start of authority (auto-managed) | Zone metadata | +| CAA | Certificate authority authorization | @ → letsencrypt.org | +| PTR | Reverse DNS lookup | 4.3.2.1 → www.contoso.com | + +### Azure DNS Limits + +| Resource | Limit | +|----------|-------| +| Public DNS zones per subscription | 250 | +| Record sets per public DNS zone | 10,000 | +| Records per record set | 20 | +| Private DNS zones per subscription | 1,000 | +| VNet links per Private DNS zone | 1,000 | +| Auto-registration VNet links per Private DNS zone | 100 | +| Private DNS zones per VNet (with auto-registration) | 1 | +| Private DNS zones per VNet (resolution only) | 1,000 | +| DNS Private Resolver inbound endpoints | 10 per resolver | +| DNS Private Resolver outbound endpoints | 10 per resolver | +| Forwarding rules per ruleset | 1,000 | + +## References + +- [Public DNS Zones](references/public-dns-zones.md) +- [Private DNS Zones](references/private-dns-zones.md) +- [DNS Private Resolver](references/dns-private-resolver.md) +- [Record Types Reference](references/record-types.md) diff --git a/plugin/skills/azure-dns/references/dns-private-resolver.md b/plugin/skills/azure-dns/references/dns-private-resolver.md new file mode 100644 index 000000000..6b611280b --- /dev/null +++ b/plugin/skills/azure-dns/references/dns-private-resolver.md @@ -0,0 +1,283 @@ +# Azure DNS Private Resolver + +## Overview + +Azure DNS Private Resolver is a managed service that bridges DNS resolution between Azure virtual networks and on-premises or external networks. It eliminates the need to deploy and manage custom DNS server VMs for hybrid DNS scenarios. + +The resolver sits inside your VNet and provides two endpoint types: +- **Inbound endpoints** — accept DNS queries from outside Azure (on-premises, other networks) and resolve them using Azure DNS (including Private DNS zones). +- **Outbound endpoints** — forward DNS queries from Azure VMs to external DNS servers (on-premises, third-party) based on forwarding rules. + +## Architecture + +``` +On-Premises Network Azure Virtual Network (Hub) +┌──────────────────┐ ┌─────────────────────────────────┐ +│ │ │ │ +│ On-prem DNS │ Queries for │ ┌─────────────────────┐ │ +│ Server │ *.internal ──────▶ │ │ Inbound Endpoint │ │ +│ (10.1.0.4) │ │ │ (10.0.0.4) │ │ +│ │ │ └──────────┬──────────┘ │ +│ │ Answers for │ │ │ +│ │ ◀── corp.contoso.com │ ▼ │ +│ │ │ Azure DNS (168.63.129.16) │ +│ │ │ + Private DNS Zones │ +│ │ │ │ +│ │ │ ┌─────────────────────┐ │ +│ │ ◀─────────────────────│ │ Outbound Endpoint │ │ +│ │ Queries for │ │ (10.0.1.4) │ │ +│ │ corp.contoso.com │ └──────────┬──────────┘ │ +│ │ │ │ │ +│ │ │ ┌──────────▼──────────┐ │ +│ │ │ │ Forwarding Ruleset │ │ +│ │ │ │ corp.contoso.com. │ │ +│ │ │ │ → 10.1.0.4:53 │ │ +│ │ │ └─────────────────────┘ │ +└──────────────────┘ └─────────────────────────────────┘ +``` + +## When to Use DNS Private Resolver vs Custom DNS VMs + +| Criteria | DNS Private Resolver | Custom DNS VMs | +|----------|---------------------|----------------| +| Management overhead | Fully managed, no patching | You manage OS, DNS software, HA | +| High availability | Built-in (zone-redundant) | You must deploy multiple VMs + load balancing | +| Cost | Pay for endpoints + queries | VM compute costs (often higher) | +| Performance | Optimized, low-latency | Depends on VM size and configuration | +| Customization | Forwarding rules only | Full DNS server features (BIND, Windows DNS) | +| DNSSEC validation | Supported | Depends on your DNS software | +| Conditional forwarding | Yes, via ruleset rules | Yes, via DNS server configuration | + +**Recommendation:** Use DNS Private Resolver unless you need advanced DNS server features like custom zone hosting, DNS-based load balancing, or complex rewrite rules. + +## Creating a DNS Private Resolver + +### Prerequisites + +- A VNet in the region where you want the resolver. +- Two dedicated subnets: one for inbound, one for outbound (minimum /28 each). +- The subnets must be delegated to `Microsoft.Network/dnsResolvers`. + +### Subnet Preparation + +```bash +# Create the inbound endpoint subnet +az network vnet subnet create \ + --resource-group MyRG \ + --vnet-name HubVNet \ + --name InboundDnsSubnet \ + --address-prefixes 10.0.0.0/28 \ + --delegations Microsoft.Network/dnsResolvers + +# Create the outbound endpoint subnet +az network vnet subnet create \ + --resource-group MyRG \ + --vnet-name HubVNet \ + --name OutboundDnsSubnet \ + --address-prefixes 10.0.1.0/28 \ + --delegations Microsoft.Network/dnsResolvers +``` + +**Subnet requirements:** +- Minimum size: /28 (16 addresses, 11 usable after Azure reserved). +- The subnet must be dedicated — no other resources can be deployed in it. +- Subnet delegation to `Microsoft.Network/dnsResolvers` is mandatory. +- Inbound and outbound endpoints must be in separate subnets. + +### Creating the Resolver + +```bash +az dns-resolver create \ + --resource-group MyRG \ + --name HubDnsResolver \ + --location eastus \ + --id /subscriptions/{sub}/resourceGroups/MyRG/providers/Microsoft.Network/virtualNetworks/HubVNet +``` + +## Inbound Endpoints + +Inbound endpoints receive DNS queries from on-premises or peered networks and resolve them against Azure DNS (168.63.129.16), which includes Private DNS zones linked to the resolver's VNet. + +**Use case:** On-premises servers need to resolve Azure Private DNS zone records (e.g., `myvm.contoso.internal` or `mystorage.privatelink.blob.core.windows.net`). + +```bash +az dns-resolver inbound-endpoint create \ + --resource-group MyRG \ + --resolver-name HubDnsResolver \ + --name InboundEndpoint \ + --location eastus \ + --ip-configurations "[{\ + private-ip-allocation-method:Dynamic,\ + id:/subscriptions/{sub}/resourceGroups/MyRG/providers/Microsoft.Network/virtualNetworks/HubVNet/subnets/InboundDnsSubnet\ + }]" +``` + +After creation, note the assigned private IP (e.g., `10.0.0.4`). Configure your on-premises DNS server to forward relevant zones to this IP. + +**On-premises DNS server configuration (example for Windows DNS):** +1. Create a conditional forwarder for `contoso.internal` pointing to `10.0.0.4`. +2. Create a conditional forwarder for `privatelink.blob.core.windows.net` pointing to `10.0.0.4`. +3. Ensure there is network connectivity (VPN or ExpressRoute) from on-premises to the inbound endpoint subnet. + +## Outbound Endpoints + +Outbound endpoints send DNS queries from Azure VMs to external DNS servers (typically on-premises). They work together with forwarding rulesets to control which domains are forwarded and where. + +```bash +az dns-resolver outbound-endpoint create \ + --resource-group MyRG \ + --resolver-name HubDnsResolver \ + --name OutboundEndpoint \ + --location eastus \ + --id /subscriptions/{sub}/resourceGroups/MyRG/providers/Microsoft.Network/virtualNetworks/HubVNet/subnets/OutboundDnsSubnet +``` + +## Forwarding Rulesets + +A forwarding ruleset is a collection of rules that define which DNS queries to forward and where. Rulesets are linked to outbound endpoints and VNets. + +### Creating a Ruleset + +```bash +az dns-resolver forwarding-ruleset create \ + --resource-group MyRG \ + --name HybridForwardingRuleset \ + --location eastus \ + --outbound-endpoints "[{\ + id:/subscriptions/{sub}/resourceGroups/MyRG/providers/Microsoft.Network/dnsResolvers/HubDnsResolver/outboundEndpoints/OutboundEndpoint\ + }]" +``` + +### Creating Forwarding Rules + +Each rule matches a domain suffix and forwards matching queries to specified target DNS servers. + +```bash +# Forward corp.contoso.com queries to on-premises DNS +az dns-resolver forwarding-rule create \ + --resource-group MyRG \ + --ruleset-name HybridForwardingRuleset \ + --name ForwardCorpDomain \ + --domain-name "corp.contoso.com." \ + --forwarding-rule-state Enabled \ + --target-dns-servers "[{ip-address:10.1.0.4,port:53},{ip-address:10.1.0.5,port:53}]" + +# Forward another on-premises domain +az dns-resolver forwarding-rule create \ + --resource-group MyRG \ + --ruleset-name HybridForwardingRuleset \ + --name ForwardLegacyDomain \ + --domain-name "legacy.internal." \ + --forwarding-rule-state Enabled \ + --target-dns-servers "[{ip-address:10.1.0.4,port:53}]" +``` + +**Rule matching:** +- Domain names must end with a trailing dot (e.g., `corp.contoso.com.`). +- Rules match the domain and all subdomains (e.g., `corp.contoso.com.` matches `app.corp.contoso.com`). +- More specific rules take precedence (longest suffix match). +- Queries that don't match any rule go to Azure DNS as normal. + +### Linking a Ruleset to VNets + +A forwarding ruleset must be linked to VNets for the rules to take effect on VMs in those VNets. + +```bash +# Link the ruleset to the hub VNet +az dns-resolver vnet-link create \ + --resource-group MyRG \ + --ruleset-name HybridForwardingRuleset \ + --name HubVNetRulesetLink \ + --id /subscriptions/{sub}/resourceGroups/MyRG/providers/Microsoft.Network/virtualNetworks/HubVNet + +# Link to spoke VNets as well +az dns-resolver vnet-link create \ + --resource-group MyRG \ + --ruleset-name HybridForwardingRuleset \ + --name Spoke1RulesetLink \ + --id /subscriptions/{sub}/resourceGroups/MyRG/providers/Microsoft.Network/virtualNetworks/Spoke1VNet +``` + +## Network Architecture: Placement Guidance + +**Place the DNS Private Resolver in the hub VNet** of a hub-spoke topology. + +``` + ┌──────────────────────────┐ + │ Hub VNet │ + On-Premises ◀═══▶│ DNS Private Resolver │ + (VPN/ER) │ ├─ Inbound Endpoint │ + │ ├─ Outbound Endpoint │ + │ └─ Forwarding Ruleset │ + │ │ + │ Private DNS Zone Links │ + └──────────┬─────────────────┘ + ┌────────┼────────┐ + ▼ ▼ ▼ + Spoke 1 Spoke 2 Spoke 3 +``` + +**Why the hub?** +- On-premises connectivity (VPN/ExpressRoute) terminates in the hub. +- The inbound endpoint needs to be reachable from on-premises. +- The outbound endpoint needs to reach on-premises DNS servers. +- Spoke VNets connect via peering — DNS queries traverse the peering link. + +**Spoke VNet DNS configuration:** +- If spokes use Azure default DNS, they automatically use 168.63.129.16, which respects forwarding ruleset links. +- If spokes use custom DNS settings pointing to the resolver inbound endpoint IP, queries go through the resolver for all names. + +## Performance and Scaling + +- Each inbound or outbound endpoint can handle up to **10,000 DNS queries per second**. +- A resolver can have up to 10 inbound and 10 outbound endpoints. +- For higher throughput, add more endpoints in the same or different subnets. +- DNS Private Resolver is zone-redundant within the region (built-in high availability). +- Latency is typically sub-millisecond within the same region. + +**Scaling example:** If you need 25,000 QPS inbound from on-premises, create 3 inbound endpoints and distribute queries across their IPs (using on-premises DNS round-robin or load balancing). + +## Troubleshooting + +### Resolver Not Forwarding Queries + +1. **Verify the forwarding ruleset is linked to the correct VNet:** + ```bash + az dns-resolver vnet-link list --ruleset-name HybridForwardingRuleset -g MyRG -o table + ``` +2. **Verify the forwarding rule domain name ends with a dot:** `corp.contoso.com.` not `corp.contoso.com`. +3. **Verify the rule state is Enabled:** + ```bash + az dns-resolver forwarding-rule show -g MyRG --ruleset-name HybridForwardingRuleset -n ForwardCorpDomain + ``` +4. **Test from a VM in the linked VNet:** + ```bash + nslookup app.corp.contoso.com + ``` + +### Timeout or No Response + +1. **Check network connectivity** between the outbound endpoint subnet and the target DNS servers. NSG rules on the outbound subnet must allow UDP/TCP port 53 outbound. +2. **Check the on-premises firewall** — it must allow DNS traffic from the outbound endpoint subnet. +3. **Verify the target DNS server is running** and accepting queries on port 53. +4. **Check the inbound endpoint reachability** from on-premises — the VPN/ExpressRoute path must allow traffic to the inbound endpoint subnet on UDP/TCP port 53. + +### Subnet Conflicts + +1. **Delegation error:** The subnet must be delegated to `Microsoft.Network/dnsResolvers`. Check: + ```bash + az network vnet subnet show -g MyRG --vnet-name HubVNet -n InboundDnsSubnet --query delegations + ``` +2. **Subnet too small:** Minimum /28. If you get size errors, resize or create a new subnet. +3. **Subnet already in use:** The subnet must be dedicated to the resolver. No VMs, NICs, or other services can share it. +4. **NSG on the subnet:** While NSGs are supported on resolver subnets, ensure they do not block DNS traffic (UDP/TCP 53 inbound for inbound endpoints, outbound for outbound endpoints). + +### On-Premises DNS Not Resolving Azure Private Zones + +1. Confirm the inbound endpoint IP is reachable from on-premises (ping or traceroute). +2. Confirm the on-premises DNS server has a conditional forwarder pointing to the inbound endpoint IP for the Azure Private DNS zone name. +3. Confirm the Private DNS zone is linked (resolution or registration link) to the resolver's VNet. +4. Test by querying the inbound endpoint directly: + ```bash + nslookup myvm.contoso.internal 10.0.0.4 + ``` diff --git a/plugin/skills/azure-dns/references/private-dns-zones.md b/plugin/skills/azure-dns/references/private-dns-zones.md new file mode 100644 index 000000000..f98f3d461 --- /dev/null +++ b/plugin/skills/azure-dns/references/private-dns-zones.md @@ -0,0 +1,255 @@ +# Azure Private DNS Zones + +## Overview + +Azure Private DNS provides a reliable and secure DNS service for your virtual networks. Private DNS zones let you use your own custom domain names instead of the Azure-provided names, with name resolution scoped entirely within your virtual network. Records in a Private DNS zone are not resolvable from the internet. + +Private DNS zones are a global resource — they are not tied to a single region. VNets from any region can link to them. + +## Core Concepts + +**Private DNS zone:** A DNS zone that is only resolvable from linked virtual networks. You control the zone name (e.g., `contoso.internal`, `app.local`, or even `contoso.com` for split-horizon). + +**VNet link:** A connection between a Private DNS zone and a virtual network. Without at least one link, the zone is useless. There are two types: +- **Resolution link** (`--registration-enabled false`): VMs in the linked VNet can resolve records in the zone, but their names are NOT automatically registered. +- **Registration link** (`--registration-enabled true`): VMs in the linked VNet can resolve records AND their names are automatically registered as A records. + +## Creating a Private DNS Zone + +```bash +# Create the zone +az network private-dns zone create \ + --resource-group MyRG \ + --name contoso.internal + +# Verify +az network private-dns zone show \ + --resource-group MyRG \ + --name contoso.internal +``` + +## VNet Links + +### Creating a Registration Link (Auto-Registration Enabled) + +```bash +az network private-dns link vnet create \ + --resource-group MyRG \ + --zone-name contoso.internal \ + --name HubVNetLink \ + --virtual-network /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/HubVNet \ + --registration-enabled true +``` + +### Creating a Resolution-Only Link + +```bash +az network private-dns link vnet create \ + --resource-group MyRG \ + --zone-name contoso.internal \ + --name SpokeVNetLink \ + --virtual-network /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/SpokeVNet \ + --registration-enabled false +``` + +### Listing and Managing Links + +```bash +# List all VNet links for a zone +az network private-dns link vnet list -g MyRG -z contoso.internal -o table + +# Delete a link +az network private-dns link vnet delete -g MyRG -z contoso.internal -n OldLink --yes +``` + +## Auto-Registration + +When a VNet link has `--registration-enabled true`, Azure automatically creates and manages DNS records for VMs in that VNet. + +**What gets registered:** +- Primary NIC's primary private IP address → A record using the VM name +- If the VM name is `webserver01`, the A record is `webserver01.contoso.internal` +- When the VM is deallocated or deleted, the record is automatically removed + +**Limitations and rules:** +- A VNet can have auto-registration enabled for only ONE Private DNS zone. +- A Private DNS zone can have up to 100 VNet links with auto-registration enabled. +- Only VMs get auto-registered — other resources (load balancers, private endpoints, etc.) must have manual records. +- Auto-registration uses the VM name, not a custom hostname. If two VMs in linked VNets have the same name, it creates a conflict. +- NICs with multiple IP configurations: only the primary IP of the primary NIC is registered. + +```bash +# Check if auto-registration is working +az network private-dns record-set a list -g MyRG -z contoso.internal -o table +``` + +## Split-Horizon DNS + +Split-horizon means using the **same zone name** for both public and private DNS. For example, `contoso.com` can be a public zone in Azure DNS and a private zone in Azure Private DNS. + +**How it works:** +- VMs in linked VNets resolve `app.contoso.com` from the Private DNS zone (returns internal IP). +- Internet clients resolve `app.contoso.com` from the public DNS zone (returns public IP). + +**Setup:** + +```bash +# Public zone (already exists or create it) +az network dns zone create -g MyRG -n contoso.com + +# Private zone with the SAME name +az network private-dns zone create -g MyRG -n contoso.com + +# Link the private zone to your VNet +az network private-dns link vnet create -g MyRG --zone-name contoso.com \ + -n InternalLink --virtual-network MyVNet --registration-enabled false + +# Add internal record to private zone +az network private-dns record-set a add-record -g MyRG -z contoso.com -n app -a 10.0.1.50 + +# Add public record to public zone +az network dns record-set a add-record -g MyRG -z contoso.com -n app -a 203.0.113.50 +``` + +**Important:** The private zone takes precedence for VMs in linked VNets. Any record in the private zone shadows the same record in the public zone for those VMs. If a record exists only in the public zone, VMs will still resolve it via the public internet. + +## Linking Multiple VNets + +A common pattern is to link multiple VNets to a single Private DNS zone for shared name resolution. + +```bash +# Link hub VNet with registration +az network private-dns link vnet create -g MyRG --zone-name contoso.internal \ + -n HubLink --virtual-network HubVNet --registration-enabled true + +# Link spoke VNets with resolution only +az network private-dns link vnet create -g MyRG --zone-name contoso.internal \ + -n Spoke1Link --virtual-network Spoke1VNet --registration-enabled false + +az network private-dns link vnet create -g MyRG --zone-name contoso.internal \ + -n Spoke2Link --virtual-network Spoke2VNet --registration-enabled false +``` + +With this setup: +- VMs in HubVNet are auto-registered. +- VMs in all three VNets can resolve records in the zone. +- VMs in spoke VNets must have records created manually (or enable registration there too, but remember the one-zone-per-VNet auto-registration limit). + +## Hub-Spoke DNS Architecture + +The recommended architecture for large environments: + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Spoke VNet │ │ Hub VNet │ │ Spoke VNet │ +│ (10.1.0.0) │────▶│ (10.0.0.0) │◀────│ (10.2.0.0) │ +│ resolution │ │ registration │ │ resolution │ +└─────────────┘ └──────┬───────┘ └─────────────┘ + │ + ┌──────▼───────┐ + │ Private DNS │ + │ Zone │ + │ contoso.int │ + └──────────────┘ +``` + +**Design principles:** +1. Place registration links on the hub VNet (or shared services VNet). +2. Place resolution-only links on spoke VNets. +3. Use VNet peering between hub and spokes so DNS queries can flow. +4. For large deployments, consider a DNS Private Resolver in the hub for on-premises integration. +5. Use separate zones for different environments (e.g., `dev.internal`, `prod.internal`). + +## Conditional Forwarding for On-Premises + +When VMs need to resolve on-premises domains (e.g., `corp.contoso.com`), you must forward those queries to your on-premises DNS servers. Private DNS zones alone cannot do this — you need either: + +1. **Azure DNS Private Resolver** (recommended) — see [DNS Private Resolver](dns-private-resolver.md). +2. **Custom DNS server VMs** in Azure that forward queries conditionally. + +If using custom DNS VMs, configure the VNet's DNS settings to point to those VMs, and configure the VMs to forward `corp.contoso.com` queries to on-premises DNS (e.g., 10.1.0.4). + +## Private DNS Zones for Private Endpoints + +Azure private endpoints use Private DNS zones to resolve the private endpoint's private IP instead of the public IP. Each Azure service has a recommended zone name. + +| Azure Service | Private DNS Zone Name | +|---------------|----------------------| +| Azure SQL Database | `privatelink.database.windows.net` | +| Azure Storage (Blob) | `privatelink.blob.core.windows.net` | +| Azure Storage (File) | `privatelink.file.core.windows.net` | +| Azure Key Vault | `privatelink.vaultcore.azure.net` | +| Azure App Service | `privatelink.azurewebsites.net` | +| Azure Container Registry | `privatelink.azurecr.io` | +| Azure Cosmos DB | `privatelink.documents.azure.com` | +| Azure Event Hubs | `privatelink.servicebus.windows.net` | +| Azure Monitor | `privatelink.monitor.azure.com` | + +**Setup pattern:** + +```bash +# Create the Private DNS zone for the service +az network private-dns zone create -g MyRG -n privatelink.database.windows.net + +# Link it to VNets that need resolution +az network private-dns link vnet create -g MyRG \ + --zone-name privatelink.database.windows.net \ + -n HubLink --virtual-network HubVNet --registration-enabled false + +# When creating a private endpoint, integrate with the zone +az network private-endpoint dns-zone-group create \ + --resource-group MyRG \ + --endpoint-name MySqlPE \ + --name default \ + --private-dns-zone /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.database.windows.net \ + --zone-name privatelink-database-windows-net +``` + +The DNS zone group automatically creates an A record in the Private DNS zone mapping the service FQDN to the private endpoint's private IP. + +## Troubleshooting + +### VMs Not Registering (Auto-Registration) + +1. **Verify the VNet link has registration enabled:** + ```bash + az network private-dns link vnet show -g MyRG -z contoso.internal -n MyLink \ + --query registrationEnabled + ``` +2. **Check if another zone already has auto-registration for that VNet.** A VNet can only auto-register with one zone. +3. **Confirm the VM is running.** Deallocated VMs have their records removed. +4. **Wait a few minutes.** Auto-registration is eventual — it can take 1-2 minutes after VM creation. + +### Resolution Failing Across Peered VNets + +1. **Verify VNet peering is active** on both sides. DNS queries flow over peering. +2. **Verify the spoke VNet has a resolution link** to the Private DNS zone. +3. **Check VNet DNS settings.** If the VNet is configured with custom DNS servers, those servers must be able to resolve the Private DNS zone (or forward to Azure's 168.63.129.16). +4. **Test from inside a VM:** + ```bash + nslookup myvm.contoso.internal + # Should return the private IP from the Private DNS zone + ``` + +### Resolution Failing for Private Endpoints + +1. **Verify the Private DNS zone exists** with the correct name for the service. +2. **Verify a VNet link exists** between the zone and the VNet where the client VM lives. +3. **Check the A record exists** in the zone: `az network private-dns record-set a list -g MyRG -z privatelink.database.windows.net` +4. **If using custom DNS servers**, they must forward `privatelink.*` zones to Azure DNS (168.63.129.16). +5. **Test resolution:** + ```bash + nslookup myserver.database.windows.net + # Should return a CNAME to myserver.privatelink.database.windows.net → private IP + ``` + +### Records Not Resolving Despite Link Existing + +1. **Check the link provisioning state:** + ```bash + az network private-dns link vnet show -g MyRG -z contoso.internal -n MyLink \ + --query provisioningState + ``` + It should be `Succeeded`. +2. **Check if the VNet uses custom DNS.** Custom DNS servers must forward to 168.63.129.16 for Private DNS zone resolution. +3. **Verify the record exists:** `az network private-dns record-set a show -g MyRG -z contoso.internal -n myvm` diff --git a/plugin/skills/azure-dns/references/public-dns-zones.md b/plugin/skills/azure-dns/references/public-dns-zones.md new file mode 100644 index 000000000..49d220fc5 --- /dev/null +++ b/plugin/skills/azure-dns/references/public-dns-zones.md @@ -0,0 +1,238 @@ +# Azure Public DNS Zones + +## Overview + +Azure DNS hosts your public DNS zones on Microsoft's global network of name servers, providing high availability and fast query performance. When you host a zone in Azure DNS, you manage your DNS records using the same credentials, APIs, tools, and billing as your other Azure services. + +Azure DNS does not support domain purchasing. To host a domain, you must own it and configure your domain registrar to delegate to the Azure DNS name servers assigned to your zone. + +## Creating a Public DNS Zone + +```bash +# Create a zone +az network dns zone create \ + --resource-group MyRG \ + --name contoso.com + +# Verify the zone and note the assigned name servers +az network dns zone show \ + --resource-group MyRG \ + --name contoso.com \ + --query nameServers \ + --output tsv +``` + +Azure assigns four name servers from the pool (e.g., `ns1-04.azure-dns.com`, `ns2-04.azure-dns.net`, `ns3-04.azure-dns.org`, `ns4-04.azure-dns.info`). These four servers span different top-level domains for resilience. + +## Delegating Your Domain to Azure DNS + +Delegation is the critical step that makes Azure DNS authoritative for your domain. At your domain registrar (GoDaddy, Namecheap, Route 53, etc.), replace the existing NS records with the four Azure DNS name servers. + +**Steps:** + +1. Create the zone in Azure DNS (see above). +2. Note the four name servers from the zone properties. +3. Log in to your registrar's management console. +4. Replace the NS records for your domain with Azure's name servers. +5. Wait for propagation (can take up to 48 hours, typically minutes to hours). + +**Verification:** + +```bash +# Check delegation from the internet +nslookup -type=NS contoso.com +# Or use dig +dig NS contoso.com +short +``` + +If delegation is correct, the response should list your Azure DNS name servers. + +**Common mistakes:** +- Forgetting the trailing dot on NS records at some registrars (e.g., `ns1-04.azure-dns.com.`). +- Changing NS records for a subdomain instead of the apex. +- Not waiting for TTL expiry on old NS records. + +## Record Management + +### Creating Records + +```bash +# A record +az network dns record-set a add-record -g MyRG -z contoso.com -n www -a 203.0.113.10 + +# AAAA record +az network dns record-set aaaa add-record -g MyRG -z contoso.com -n www -a 2001:db8::1 + +# CNAME record +az network dns record-set cname set-record -g MyRG -z contoso.com -n blog -c blogapp.azurewebsites.net + +# MX record +az network dns record-set mx add-record -g MyRG -z contoso.com -n @ -e mail.contoso.com -p 10 + +# TXT record (SPF) +az network dns record-set txt add-record -g MyRG -z contoso.com -n @ \ + -v "v=spf1 include:spf.protection.outlook.com -all" + +# Multiple records in one record set (round-robin) +az network dns record-set a add-record -g MyRG -z contoso.com -n www -a 203.0.113.10 +az network dns record-set a add-record -g MyRG -z contoso.com -n www -a 203.0.113.11 +``` + +### Updating Records + +```bash +# Update TTL on a record set +az network dns record-set a update -g MyRG -z contoso.com -n www --set ttl=300 +``` + +### Deleting Records + +```bash +# Remove a specific record from a record set +az network dns record-set a remove-record -g MyRG -z contoso.com -n www -a 203.0.113.10 + +# Delete an entire record set +az network dns record-set a delete -g MyRG -z contoso.com -n www --yes +``` + +### Listing Records + +```bash +# List all record sets in a zone +az network dns record-set list -g MyRG -z contoso.com -o table + +# List only A records +az network dns record-set a list -g MyRG -z contoso.com -o table +``` + +## Alias Record Sets + +Alias records are an Azure DNS extension that allows a record set to refer to an Azure resource instead of a static IP. The key advantage: when the Azure resource's IP changes, the DNS record automatically updates. + +**Supported alias targets:** +- Azure Public IP address +- Azure Traffic Manager profile +- Azure CDN endpoint +- Azure Front Door (classic) +- Another record set in the same zone + +**Creating an alias record:** + +```bash +# Alias A record at zone apex pointing to a public IP +az network dns record-set a create -g MyRG -z contoso.com -n @ \ + --target-resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/publicIPAddresses/MyPublicIP + +# Alias CNAME pointing to a Traffic Manager profile +az network dns record-set cname create -g MyRG -z contoso.com -n app \ + --target-resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/trafficManagerProfiles/MyTMProfile +``` + +### Zone Apex Limitations + +The DNS protocol forbids CNAME records at the zone apex (`@` or bare domain like `contoso.com`). This creates a problem when you want to point your bare domain to an Azure load balancer, App Service, or CDN that only provides a hostname. + +**Alias records solve this.** You can create an alias A or AAAA record at the apex that tracks the IP of an Azure resource. The record appears as a standard A record to DNS clients, but Azure DNS automatically resolves the underlying resource IP. + +```bash +# Point contoso.com (apex) to an Azure load balancer public IP +az network dns record-set a create -g MyRG -z contoso.com -n @ \ + --target-resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/publicIPAddresses/LBPublicIP +``` + +## TTL Guidance + +TTL (Time To Live) controls how long resolvers cache your record before re-querying. Set it per record set. + +| Scenario | Recommended TTL | Reason | +|----------|----------------|--------| +| Stable production records | 3600s (1 hour) | Reduces query volume and cost | +| Pre-migration (lower in advance) | 60–300s | Ensures fast cutover when IP changes | +| During migration/cutover | 60s | Minimizes stale cache impact | +| Post-migration (raise back) | 3600s | Return to normal caching | +| Alias records | 0–60s or omit | Azure DNS refreshes alias targets automatically | + +**Important:** Lower the TTL *before* a planned change. If your current TTL is 3600s, lower it at least 1 hour before the change so caches expire. + +```bash +# Lower TTL before migration +az network dns record-set a update -g MyRG -z contoso.com -n www --set ttl=60 + +# After migration stabilizes, raise TTL +az network dns record-set a update -g MyRG -z contoso.com -n www --set ttl=3600 +``` + +## Subdomain Delegation + +You can delegate a subdomain to a child zone within Azure DNS (or to another DNS provider). + +```bash +# Create the child zone +az network dns zone create -g MyRG -n staging.contoso.com + +# Get the child zone's name servers +az network dns zone show -g MyRG -n staging.contoso.com --query nameServers -o tsv + +# In the parent zone, create NS records for the subdomain +az network dns record-set ns add-record -g MyRG -z contoso.com -n staging -d ns1-08.azure-dns.com +az network dns record-set ns add-record -g MyRG -z contoso.com -n staging -d ns2-08.azure-dns.net +az network dns record-set ns add-record -g MyRG -z contoso.com -n staging -d ns3-08.azure-dns.org +az network dns record-set ns add-record -g MyRG -z contoso.com -n staging -d ns4-08.azure-dns.info +``` + +Each child zone is managed independently and can be in a different resource group or subscription, enabling team-level DNS management. + +## DNSSEC Support + +Azure DNS supports DNSSEC (Domain Name System Security Extensions) for public zones. DNSSEC adds cryptographic signatures to DNS records, allowing resolvers to verify that responses have not been tampered with. + +**Enabling DNSSEC:** + +```bash +# Enable DNSSEC signing on the zone +az network dns dnssec-config create -g MyRG -z contoso.com +``` + +After enabling, Azure DNS signs your zone. You must then add DS (Delegation Signer) records at your registrar to complete the chain of trust. + +**Important considerations:** +- DNSSEC adds computational overhead to signing — negligible for most zones. +- If you disable DNSSEC, remove DS records from your registrar first to avoid validation failures. +- Not all registrars support DS record management — verify before enabling. + +## Pricing Model + +Azure DNS pricing has two components: + +1. **Zone hosting:** Per zone per month (first 25 zones at one rate, additional zones at a reduced rate). +2. **DNS queries:** Per million queries (first 1 billion queries at one rate, additional queries at a reduced rate). + +Alias record queries against Azure resources (public IP, Traffic Manager, etc.) are free. This makes alias records cost-effective for high-traffic apex domains. + +Check [Azure DNS pricing](https://azure.microsoft.com/pricing/details/dns/) for current rates. + +## Troubleshooting + +### Delegation Not Working + +1. **Verify NS records at registrar** — use `dig NS contoso.com` from an external resolver. The response must show Azure DNS name servers. +2. **Check for typos** in name server names. They must exactly match the values from `az network dns zone show`. +3. **Wait for propagation** — old NS records may be cached. Check the TTL on the previous NS records. +4. **Test from multiple locations** — use tools like `dig @8.8.8.8 NS contoso.com` to query specific resolvers. + +### Records Not Resolving + +1. **Confirm the record exists:** `az network dns record-set a show -g MyRG -z contoso.com -n www` +2. **Check TTL caching:** Resolvers cache records for the TTL duration. Use `dig www.contoso.com +trace` to bypass cache. +3. **Verify delegation first** — if delegation is broken, no records resolve. +4. **Check for CNAME conflicts:** A CNAME at `www` blocks any other record type at `www`. + +### TTL Cache Issues + +- Records appear to return old values: the resolver is serving cached data. Wait for the TTL to expire or flush your local DNS cache (`ipconfig /flushdns` on Windows, `sudo dscacheutil -flushcache` on macOS). +- After lowering TTL, the change itself is subject to the *previous* TTL — plan ahead. + +### Zone Not Appearing in Portal + +- Verify the resource group and subscription. Use `az network dns zone list --output table` to find all zones. +- Check Azure RBAC — you need at least Reader on the zone to see it. diff --git a/plugin/skills/azure-dns/references/record-types.md b/plugin/skills/azure-dns/references/record-types.md new file mode 100644 index 000000000..1c0361162 --- /dev/null +++ b/plugin/skills/azure-dns/references/record-types.md @@ -0,0 +1,286 @@ +# DNS Record Types Reference + +## Overview + +Azure DNS supports all standard DNS record types for both public and private zones. This reference covers each type with its purpose, syntax, Azure CLI commands, and common pitfalls. + +In Azure DNS, records are organized into **record sets**. A record set is a collection of records with the same name and type. For example, two A records for `www` (round-robin) form a single record set. + +--- + +## A Record (Address) + +**Purpose:** Maps a hostname to an IPv4 address. + +**When to use:** Pointing a name to a server, load balancer, or any resource with an IPv4 address. + +```bash +# Create and add an A record +az network dns record-set a add-record -g MyRG -z contoso.com -n www -a 203.0.113.10 + +# Add a second record for round-robin +az network dns record-set a add-record -g MyRG -z contoso.com -n www -a 203.0.113.11 + +# Remove a specific record +az network dns record-set a remove-record -g MyRG -z contoso.com -n www -a 203.0.113.11 +``` + +**Common mistakes:** +- Using a CNAME where an A record is needed (e.g., at the zone apex). +- Forgetting that A records at the same name form a record set — adding a second A record does not replace the first. + +--- + +## AAAA Record (IPv6 Address) + +**Purpose:** Maps a hostname to an IPv6 address. + +**When to use:** Pointing a name to a resource with an IPv6 address. Identical to A records but for IPv6. + +```bash +az network dns record-set aaaa add-record -g MyRG -z contoso.com -n www -a 2001:db8::1 +``` + +**Common mistakes:** +- Formatting errors in IPv6 addresses (use proper colon notation). +- Creating AAAA records without verifying the target actually has IPv6 connectivity. + +--- + +## CNAME Record (Canonical Name) + +**Purpose:** Creates an alias from one name to another hostname. The DNS resolver follows the chain to the final A/AAAA record. + +**When to use:** Pointing a subdomain to another service's hostname (e.g., App Service, CDN). + +```bash +az network dns record-set cname set-record -g MyRG -z contoso.com -n blog -c blogapp.azurewebsites.net +``` + +**Key rules:** +- **Cannot coexist** with any other record type at the same name. If `blog` has a CNAME, you cannot also have an A, TXT, or MX at `blog`. +- **Cannot be used at the zone apex** (`@` or bare domain). Use an alias record instead. +- A CNAME record set can contain only ONE record (not a set of multiple). +- Uses `set-record` instead of `add-record` because only one value is allowed. + +**Common mistakes:** +- Attempting to create a CNAME at the zone apex — this violates the DNS RFC. +- Creating a CNAME where other record types already exist at that name. +- Creating a CNAME chain (CNAME → CNAME → CNAME) — while technically valid, it adds latency and complexity. + +--- + +## MX Record (Mail Exchange) + +**Purpose:** Directs email delivery for the domain to a mail server. + +**When to use:** Configuring email routing. Required for receiving email at your domain. + +```bash +# Primary mail server (priority 10) +az network dns record-set mx add-record -g MyRG -z contoso.com -n @ -e mail.contoso.com -p 10 + +# Backup mail server (priority 20 — higher number = lower priority) +az network dns record-set mx add-record -g MyRG -z contoso.com -n @ -e backup-mail.contoso.com -p 20 +``` + +**Parameters:** +- `-e` / `--exchange`: The mail server hostname. +- `-p` / `--preference`: Priority value (lower number = higher priority). + +**Common mistakes:** +- Confusing priority direction — MX priority 10 is preferred over 20. +- Using an IP address instead of a hostname for the exchange value (MX requires a hostname). +- Forgetting to create an A record for the mail server hostname. + +--- + +## TXT Record (Text) + +**Purpose:** Stores arbitrary text data. Used for domain verification, SPF, DKIM, DMARC, and other metadata. + +**When to use:** Email authentication (SPF/DKIM/DMARC), domain ownership verification (Microsoft 365, Google Workspace, SSL certificates), custom metadata. + +```bash +# SPF record +az network dns record-set txt add-record -g MyRG -z contoso.com -n @ \ + -v "v=spf1 include:spf.protection.outlook.com -all" + +# Domain verification for Microsoft 365 +az network dns record-set txt add-record -g MyRG -z contoso.com -n @ \ + -v "MS=ms12345678" + +# DMARC record +az network dns record-set txt add-record -g MyRG -z contoso.com -n _dmarc \ + -v "v=DMARC1; p=reject; rua=mailto:dmarc@contoso.com" +``` + +**Common mistakes:** +- TXT records longer than 255 characters must be split into multiple strings within a single record. Azure CLI handles this automatically for most cases. +- Adding multiple SPF records — you should have only one SPF TXT record at the same name. Combine with `include:` instead. + +--- + +## SRV Record (Service Locator) + +**Purpose:** Specifies the location (hostname and port) of a service. + +**When to use:** Service discovery for protocols like SIP, XMPP, LDAP, or custom services. + +```bash +# SIP over TCP service +az network dns record-set srv add-record -g MyRG -z contoso.com \ + -n _sip._tcp -r sipserver.contoso.com -p 5060 -w 10 -t 0 +``` + +**Parameters:** +- `-n`: Name in format `_service._protocol` (e.g., `_sip._tcp`). +- `-r` / `--target`: The hostname providing the service. +- `-p` / `--port`: The port number. +- `-w` / `--weight`: Relative weight for load balancing between same-priority records. +- `-t` / `--priority`: Lower number = higher priority. + +**Common mistakes:** +- Forgetting the underscore prefix on service and protocol names. +- Using the wrong protocol (TCP vs UDP) for the service. + +--- + +## NS Record (Name Server) + +**Purpose:** Delegates a subdomain to a different set of name servers. + +**When to use:** Subdomain delegation — handing off DNS management for a subdomain to another zone. + +```bash +# Delegate staging.contoso.com to a child zone +az network dns record-set ns add-record -g MyRG -z contoso.com -n staging \ + -d ns1-08.azure-dns.com +az network dns record-set ns add-record -g MyRG -z contoso.com -n staging \ + -d ns2-08.azure-dns.net +``` + +**Key rules:** +- NS records at the zone apex are managed by Azure DNS and cannot be modified. +- You can create NS records for subdomains to delegate to other zones. + +**Common mistakes:** +- Trying to edit the apex NS records (Azure manages these). +- Not adding all four Azure DNS name servers when delegating to an Azure child zone. + +--- + +## SOA Record (Start of Authority) + +**Purpose:** Contains metadata about the zone: primary name server, admin email, serial number, refresh/retry/expire timers. + +**When to use:** You typically do not create or delete SOA records — Azure DNS manages them automatically. You may update the email and TTL. + +```bash +# View the SOA record +az network dns record-set soa show -g MyRG -z contoso.com + +# Update the admin email +az network dns record-set soa update -g MyRG -z contoso.com -e admin.contoso.com +``` + +**Key rules:** +- Every zone has exactly one SOA record at the apex. +- Azure DNS manages the serial number automatically. +- The SOA email replaces `@` with `.` in the DNS wire format (e.g., `admin.contoso.com` means `admin@contoso.com`). + +--- + +## CAA Record (Certificate Authority Authorization) + +**Purpose:** Specifies which certificate authorities (CAs) are allowed to issue certificates for the domain. + +**When to use:** Restricting certificate issuance to authorized CAs for security. + +```bash +# Allow only Let's Encrypt to issue certificates +az network dns record-set caa add-record -g MyRG -z contoso.com -n @ \ + -f 0 -t issue -v "letsencrypt.org" + +# Allow DigiCert for wildcard certificates +az network dns record-set caa add-record -g MyRG -z contoso.com -n @ \ + -f 0 -t issuewild -v "digicert.com" + +# Send violation reports to an email +az network dns record-set caa add-record -g MyRG -z contoso.com -n @ \ + -f 0 -t iodef -v "mailto:security@contoso.com" +``` + +**Parameters:** +- `-f` / `--flags`: 0 for standard (non-critical), 128 for critical. +- `-t` / `--tag`: `issue` (standard certs), `issuewild` (wildcard certs), `iodef` (violation reporting). +- `-v` / `--value`: The CA domain or reporting URI. + +**Common mistakes:** +- Forgetting to add CAA records for all CAs you use — a missing CA means it cannot issue for your domain. +- Not setting `issuewild` separately from `issue` — wildcard issuance needs its own rule. + +--- + +## PTR Record (Pointer) + +**Purpose:** Reverse DNS lookup — maps an IP address back to a hostname. + +**When to use:** Reverse DNS zones (in-addr.arpa for IPv4, ip6.arpa for IPv6). Common for mail server verification and network diagnostics. + +```bash +# In a reverse zone (e.g., 113.0.203.in-addr.arpa) +az network dns record-set ptr add-record -g MyRG -z 113.0.203.in-addr.arpa \ + -n 10 -d www.contoso.com +``` + +**Key rules:** +- PTR records live in reverse DNS zones, not forward zones. +- Azure supports reverse DNS for Azure-owned public IP addresses — configure via the public IP resource. +- For non-Azure IPs, your ISP or IP address provider manages the reverse zone. + +--- + +## Alias Records (Azure-Specific) + +Alias records are not a DNS standard but an Azure DNS feature. An alias record set points to an Azure resource instead of a static value. + +**Supported types:** A, AAAA, CNAME (as alias). + +**Supported targets:** Public IP, Traffic Manager profile, CDN endpoint, Front Door, another record set in the same zone. + +```bash +# Alias A record at apex pointing to a public IP +az network dns record-set a create -g MyRG -z contoso.com -n @ \ + --target-resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/publicIPAddresses/MyPIP +``` + +**Key benefit:** The record automatically tracks the target resource's IP. If the public IP changes (e.g., after redeployment), the DNS record updates without manual intervention. + +--- + +## Wildcard Records + +Azure DNS supports wildcard records using `*` as the record name. + +```bash +# Wildcard A record — matches any name not explicitly defined +az network dns record-set a add-record -g MyRG -z contoso.com -n "*" -a 203.0.113.99 +``` + +**Behavior:** A query for `anything.contoso.com` returns the wildcard record, unless an explicit record exists for that name. Explicit records always take precedence over wildcards. + +**Common mistakes:** +- Expecting wildcards to match multi-level subdomains — `*.contoso.com` matches `foo.contoso.com` but NOT `bar.foo.contoso.com`. +- Forgetting to quote the `*` in shell commands. + +--- + +## Record Set Concepts + +- A **record set** groups all records of the same name and type together. +- You can have multiple A records in one record set (round-robin load balancing). +- CNAME and SOA record sets can contain only one record each. +- Each record set has a single TTL that applies to all records in the set. +- The maximum number of records in a record set is 20. +- Empty record sets (no records) are visible in Azure DNS but do not return results to DNS queries. diff --git a/plugin/skills/azure-expressroute/SKILL.md b/plugin/skills/azure-expressroute/SKILL.md new file mode 100644 index 000000000..040459d7e --- /dev/null +++ b/plugin/skills/azure-expressroute/SKILL.md @@ -0,0 +1,132 @@ +--- +name: azure-expressroute +description: "Provision and manage Azure ExpressRoute circuits for private dedicated connectivity to Azure, including private peering, Microsoft peering, Global Reach, ExpressRoute Direct, and FastPath data-plane acceleration. WHEN: expressroute, express route, private connectivity, dedicated circuit, Microsoft peering, private peering, Global Reach, ExpressRoute Direct, FastPath, hybrid connectivity private. DO NOT USE FOR: VPN over internet (use azure-vpn-gateway), managed hub networking (use azure-virtual-wan for ExpressRoute + vWAN integration)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure ExpressRoute + +## When to Use This Skill + +- Creating or managing ExpressRoute circuits for private dedicated connectivity to Azure +- Configuring private peering to reach Azure VNets over a dedicated connection +- Setting up Microsoft peering with route filters to access Microsoft 365 and Azure PaaS services +- Connecting two on-premises locations through the Azure backbone via Global Reach +- Deploying ExpressRoute Direct for 10 Gbps or 100 Gbps port-level connectivity +- Enabling FastPath to bypass the ExpressRoute gateway for improved data-path performance +- Selecting circuit bandwidth, SKU tier (Local, Standard, Premium), and metering plan (Metered, Unlimited) +- Troubleshooting circuit provisioning states, BGP peering, or route advertisement issues +- Designing VPN over ExpressRoute or VPN failover alongside ExpressRoute (see azure-vpn-gateway) +- Integrating ExpressRoute with Virtual WAN hubs (see azure-virtual-wan) + +## Rules + +1. **Circuit requires provider provisioning.** After creating the circuit resource in Azure, the connectivity provider must provision it. Circuit stays in `NotProvisioned` state until the provider completes layer 2 setup. +2. **ExpressRoute Direct skips the provider.** With ExpressRoute Direct, you own the physical port pair and can create circuits on it directly. Available in 10 Gbps and 100 Gbps. +3. **Private peering for VNets.** Use private peering to reach resources in Azure VNets. Requires a /30 or /126 subnet pair for primary and secondary BGP sessions. +4. **Microsoft peering for PaaS/M365.** Route filters are mandatory for Microsoft peering — you must explicitly select which service communities to advertise. +5. **Premium add-on for cross-geo.** Standard circuits connect to regions within the same geopolitical boundary. Enable Premium for global VNet linking and increased route limits (10,000 routes vs 4,000). +6. **Local SKU for same-metro.** If your peering location is in the same metro as your Azure region, use Local SKU for unlimited egress at no data transfer cost. +7. **Gateway is still required.** Even with ExpressRoute, you need an ExpressRoute gateway in the VNet to terminate the connection. FastPath can bypass it for data traffic but gateway is still needed for control plane. +8. **Redundancy is built-in.** Every circuit has two connections (primary and secondary) for built-in redundancy. Design on-prem connectivity to use both paths. +9. **BGP ASN restrictions.** Azure uses ASN 12076 for ExpressRoute peering. Do not use ASN 12076, 65515, or 65520 on on-premises routers. +10. **Deprovisioning order matters.** Remove all VNet connections first, then delete peerings, then deprovision the circuit. Deleting out of order causes orphaned resources. + +## MCP Tools + +| Tool | Operation | Purpose | +|------|-----------|---------| +| `azure__network` | `expressroute_circuit_list` | List all ExpressRoute circuits in a subscription or resource group | +| `azure__network` | `expressroute_circuit_get` | Get detailed configuration of a specific ExpressRoute circuit | + +## CLI Fallback + +```bash +# List ExpressRoute circuits +az network express-route list --resource-group + +# Show circuit details and provisioning state +az network express-route show --name --resource-group + +# Create an ExpressRoute circuit +az network express-route create \ + --name \ + --resource-group \ + --provider \ + --peering-location \ + --bandwidth 1000 \ + --sku-tier Standard \ + --sku-family MeteredData + +# Create private peering +az network express-route peering create \ + --circuit-name \ + --resource-group \ + --peering-type AzurePrivatePeering \ + --peer-asn \ + --primary-peer-subnet \ + --secondary-peer-subnet \ + --vlan-id + +# Create Microsoft peering +az network express-route peering create \ + --circuit-name \ + --resource-group \ + --peering-type MicrosoftPeering \ + --peer-asn \ + --primary-peer-subnet \ + --secondary-peer-subnet \ + --vlan-id \ + --advertised-public-prefixes + +# List peerings on a circuit +az network express-route peering list \ + --circuit-name \ + --resource-group + +# Create ExpressRoute gateway +az network vnet-gateway create \ + --name \ + --resource-group \ + --vnet \ + --gateway-type ExpressRoute \ + --sku ErGw2AZ + +# Connect circuit to VNet gateway +az network vpn-connection create \ + --name \ + --resource-group \ + --vnet-gateway1 \ + --express-route-circuit2 + +# Show BGP peering status +az network express-route peering show \ + --circuit-name \ + --resource-group \ + --name AzurePrivatePeering +``` + +## Key Concepts + +- **Circuit states:** ServiceProviderProvisioningState cycles through `NotProvisioned` → `Provisioning` → `Provisioned`. CircuitProvisioningState must be `Succeeded`. +- **SKU tiers:** Local (same-metro, free egress), Standard (same geopolitical region, 4000 routes), Premium (global, 10000 routes, more VNet links). +- **Peering types:** AzurePrivatePeering (VNet access), MicrosoftPeering (Microsoft 365, Azure PaaS via public IPs). Azure public peering is deprecated. +- **Gateway SKUs:** ErGw1Az (1 Gbps), ErGw2Az (2 Gbps), ErGw3Az (10 Gbps), ErGwScale (per-unit scaling). Ultra Performance enables FastPath. +- **FastPath:** Bypasses the ExpressRoute gateway in the data path for improved latency. Requires ErGw3Az or Ultra Performance gateway. Does not support VNet peering or UDR scenarios in all cases. +- **Global Reach:** Connects two on-premises sites through the Microsoft backbone via their respective ExpressRoute circuits. No need for a VPN tunnel between sites. +- **ExpressRoute Direct:** Physical port pairs (10G or 100G) at peering locations. Supports MACsec encryption on the ports. Create multiple circuits on one port pair. +- **BFD (Bidirectional Forwarding Detection):** Supported on private peering for sub-second failover detection. +- **Route limits:** Standard = 4,000 routes per peering. Premium = 10,000 routes per peering. Exceeding limits causes BGP session drops. +- **Coexistence with VPN:** ExpressRoute and VPN Gateway can coexist on the same VNet for VPN-as-failover scenarios (see azure-vpn-gateway). + +## References + +- [references/circuit-provisioning.md](references/circuit-provisioning.md) — Circuit creation, bandwidth, SKU tiers, provider workflow +- [references/peering-config.md](references/peering-config.md) — Private and Microsoft peering configuration +- [references/global-reach.md](references/global-reach.md) — Global Reach setup and supported regions +- [references/fastpath.md](references/fastpath.md) — FastPath configuration and limitations +- [Azure ExpressRoute documentation](https://learn.microsoft.com/azure/expressroute/) +- [ExpressRoute FAQ](https://learn.microsoft.com/azure/expressroute/expressroute-faqs) diff --git a/plugin/skills/azure-expressroute/references/circuit-provisioning.md b/plugin/skills/azure-expressroute/references/circuit-provisioning.md new file mode 100644 index 000000000..e356c18db --- /dev/null +++ b/plugin/skills/azure-expressroute/references/circuit-provisioning.md @@ -0,0 +1,265 @@ +# ExpressRoute Circuit Provisioning + +## Circuit Creation Workflow + +ExpressRoute circuit provisioning involves coordination between Azure and a connectivity provider (unless using ExpressRoute Direct). + +### Provider-Based Circuit Flow + +``` +1. Create circuit in Azure → CircuitProvisioningState: Succeeded + ServiceProviderProvisioningState: NotProvisioned +2. Send service key to provider → Provider begins layer 2 setup +3. Provider provisions circuit → ServiceProviderProvisioningState: Provisioning +4. Provider completes setup → ServiceProviderProvisioningState: Provisioned +5. Configure peering in Azure → Peering active, BGP sessions established +6. Connect gateway to circuit → VNet connectivity live +``` + +### Create a Circuit + +```bash +# Create ExpressRoute circuit via provider +az network express-route create \ + --name \ + --resource-group \ + --provider \ + --peering-location \ + --bandwidth 1000 \ + --sku-tier Standard \ + --sku-family MeteredData + +# Get the service key (send this to your provider) +az network express-route show \ + --name \ + --resource-group \ + --query serviceKey +``` + +### Check Provisioning Status + +```bash +az network express-route show \ + --name \ + --resource-group \ + --query "{circuitState:circuitProvisioningState, providerState:serviceProviderProvisioningState}" +``` + +**Expected progression:** +| Phase | circuitProvisioningState | serviceProviderProvisioningState | +|-------|--------------------------|----------------------------------| +| After Azure creation | Succeeded (or Enabled) | NotProvisioned | +| Provider working | Succeeded | Provisioning | +| Provider done | Succeeded | Provisioned | +| Peering configured | Succeeded | Provisioned | + +## Bandwidth Options + +| Bandwidth | Use Case | +|-----------|----------| +| 50 Mbps | Dev/test, small branch | +| 100 Mbps | Small production workloads | +| 200 Mbps | Medium workloads | +| 500 Mbps | Data transfer, backup traffic | +| 1 Gbps | Standard production | +| 2 Gbps | Heavy production workloads | +| 5 Gbps | Large enterprise | +| 10 Gbps | High-throughput, data-heavy apps | +| 100 Gbps | ExpressRoute Direct only | + +**Bandwidth upgrades** can be done in-place without downtime (provider must support the new bandwidth): + +```bash +az network express-route update \ + --name \ + --resource-group \ + --bandwidth 2000 +``` + +**Bandwidth downgrades** require circuit recreation — you cannot decrease bandwidth on an existing circuit. + +## SKU Tiers + +### Local SKU + +- Available only when the peering location is in the **same metro** as the target Azure region +- **Unlimited egress at no data transfer cost** (only the circuit fee applies) +- Can only connect to VNets in the local Azure region (same metro) +- Cannot enable Premium add-on +- Best for: workloads entirely within one Azure region co-located with your peering point + +```bash +az network express-route create \ + --name \ + --resource-group \ + --provider \ + --peering-location "Silicon Valley" \ + --bandwidth 1000 \ + --sku-tier Local \ + --sku-family UnlimitedData +``` + +### Standard SKU + +- Connects to VNets **within the same geopolitical region** +- Supports up to **4,000 route prefixes** on private peering +- Supports up to **10 VNet connections** (or 200 with Standard circuit linking) +- Metered or unlimited data plans available +- Best for: production workloads within a single geopolitical boundary + +### Premium SKU + +- Connects to VNets in **any Azure region globally** +- Supports up to **10,000 route prefixes** on private peering +- Supports up to **100 VNet connections** per circuit +- Required for cross-geo connectivity (e.g., North America peering location → Europe VNet) +- Required for Microsoft 365 connectivity +- Additional monthly cost on top of Standard +- Best for: global enterprises, multi-region deployments, M365 access + +```bash +# Enable Premium add-on on existing circuit +az network express-route update \ + --name \ + --resource-group \ + --sku-tier Premium +``` + +## Metering Plans + +| Plan | Billing Model | +|------|---------------| +| **MeteredData** | Circuit fee + per-GB egress charge. Ingress is free. Best when egress traffic is predictable or moderate. | +| **UnlimitedData** | Flat circuit fee with unlimited egress. Best when egress traffic is high or unpredictable. | + +Switch between plans without downtime: + +```bash +az network express-route update \ + --name \ + --resource-group \ + --sku-family UnlimitedData +``` + +## ExpressRoute Direct + +ExpressRoute Direct provides **dedicated physical port pairs** at a peering location, bypassing the provider entirely. + +### Key Characteristics + +- Available in **10 Gbps** and **100 Gbps** port speeds +- You own the physical port pair and can create multiple circuits on it +- Supports **MACsec** encryption on the port layer (point-to-point encryption) +- Circuit bandwidth on Direct can use any value from 1 Gbps to the port speed +- Supports both Standard and Premium SKUs +- No provider needed — you manage layer 2 directly + +### Create ExpressRoute Direct + +```bash +# Create the Direct port resource +az network express-route port create \ + --name \ + --resource-group \ + --peering-location \ + --bandwidth 100 \ + --encapsulation Dot1Q + +# Create a circuit on the Direct port +az network express-route create \ + --name \ + --resource-group \ + --express-route-port \ + --bandwidth 10 \ + --sku-tier Premium \ + --sku-family UnlimitedData +``` + +### MACsec Encryption (ExpressRoute Direct Only) + +MACsec encrypts traffic at layer 2 between your edge router and the Microsoft edge router. + +```bash +# Enable MACsec on a Direct port +az network express-route port update \ + --name \ + --resource-group \ + --macsec-ckn-secret-identifier \ + --macsec-cak-secret-identifier \ + --macsec-cipher GcmAes256 +``` + +## ExpressRoute Gateway + +A VNet still requires an ExpressRoute gateway to connect to a circuit. + +### Gateway SKUs + +| Gateway SKU | Max Throughput | Max Circuits | Max VNet Connections | FastPath | +|-------------|---------------|-------------|---------------------|----------| +| ErGw1Az | 1 Gbps | 4 | Up to 1,900 routes | No | +| ErGw2Az | 2 Gbps | 8 | Up to 1,900 routes | No | +| ErGw3Az | 10 Gbps | 16 | Up to 1,900 routes | Yes | +| Ultra Performance | 10 Gbps | 16 | Up to 1,900 routes | Yes | +| ErGwScale | Up to 40 Gbps | 16 | Up to 1,900 routes | Yes (2+ units) | + +```bash +# Create zone-redundant ExpressRoute gateway +az network vnet-gateway create \ + --name \ + --resource-group \ + --vnet \ + --gateway-type ExpressRoute \ + --sku ErGw2Az \ + --public-ip-addresses +``` + +### Connect Circuit to Gateway + +```bash +# Get circuit resource ID +CIRCUIT_ID=$(az network express-route show \ + --name \ + --resource-group \ + --query id -o tsv) + +# Create the connection +az network vpn-connection create \ + --name \ + --resource-group \ + --vnet-gateway1 \ + --express-route-circuit2 $CIRCUIT_ID +``` + +## Deprovisioning Order + +Always follow this order to avoid orphaned resources: + +1. **Remove all VNet gateway connections** to the circuit +2. **Delete peering configurations** on the circuit +3. **Request provider to deprovision** the circuit +4. **Wait for provider state to become NotProvisioned** +5. **Delete the circuit resource** in Azure + +```bash +# Step 1: Delete VNet connection +az network vpn-connection delete --name --resource-group + +# Step 2: Delete peerings +az network express-route peering delete \ + --circuit-name \ + --resource-group \ + --name AzurePrivatePeering + +# Steps 3-4: Contact provider, wait for NotProvisioned + +# Step 5: Delete circuit +az network express-route delete --name --resource-group +``` + +## Additional References + +- [Create ExpressRoute circuit](https://learn.microsoft.com/azure/expressroute/expressroute-howto-circuit-arm) +- [ExpressRoute locations and providers](https://learn.microsoft.com/azure/expressroute/expressroute-locations) +- [ExpressRoute Direct](https://learn.microsoft.com/azure/expressroute/expressroute-erdirect-about) +- [ExpressRoute pricing](https://azure.microsoft.com/pricing/details/expressroute/) diff --git a/plugin/skills/azure-expressroute/references/fastpath.md b/plugin/skills/azure-expressroute/references/fastpath.md new file mode 100644 index 000000000..2e2ec2612 --- /dev/null +++ b/plugin/skills/azure-expressroute/references/fastpath.md @@ -0,0 +1,180 @@ +# ExpressRoute FastPath + +## Overview + +FastPath is a data-path optimization feature that bypasses the ExpressRoute virtual network gateway for traffic flowing between on-premises and Azure VNets. With FastPath, data-plane traffic is sent directly to VMs in the VNet, reducing latency and improving throughput. + +**Without FastPath:** +``` +On-Prem → ExpressRoute Circuit → ExpressRoute Gateway → VNet (VMs) +``` + +**With FastPath:** +``` +On-Prem → ExpressRoute Circuit → VNet (VMs) directly + ↑ + ExpressRoute Gateway still handles control plane +``` + +The gateway is still required for control-plane operations (route exchange, connection management) but data-plane packets skip it. + +## When to Use FastPath + +- Latency-sensitive workloads where even the gateway hop adds noticeable delay +- High-throughput data transfers exceeding the gateway SKU's maximum bandwidth +- Applications requiring consistent low-latency connectivity (financial trading, real-time databases) +- Workloads currently bottlenecked by the ER gateway's throughput limits + +## Requirements + +### Gateway SKU Requirements + +FastPath is only available on high-performance gateway SKUs: + +| Gateway SKU | FastPath Support | +|-------------|-----------------| +| ErGw1Az | No | +| ErGw2Az | No | +| ErGw3Az | Yes | +| Ultra Performance (ErGw3Az equivalent) | Yes | +| ErGwScale (2+ scale units) | Yes | + +If you are on ErGw1Az or ErGw2Az, you must upgrade the gateway before enabling FastPath. + +### Circuit Requirements + +- ExpressRoute circuit must be in **Provisioned** state with active private peering +- Works with both provider-based circuits and ExpressRoute Direct circuits +- No specific circuit bandwidth requirement — FastPath works on any bandwidth + +## Enabling FastPath + +FastPath is enabled on the **connection** between the ER gateway and the circuit, not on the gateway itself. + +```bash +# Enable FastPath on an existing connection +az network vpn-connection update \ + --name \ + --resource-group \ + --express-route-gateway-bypass true + +# Create a new connection with FastPath enabled +az network vpn-connection create \ + --name \ + --resource-group \ + --vnet-gateway1 \ + --express-route-circuit2 \ + --express-route-gateway-bypass true +``` + +### Verify FastPath Status + +```bash +az network vpn-connection show \ + --name \ + --resource-group \ + --query "{name:name, fastPath:expressRouteGatewayBypass, status:connectionStatus}" +``` + +## Supported Scenarios + +FastPath works with the following configurations: + +| Scenario | Supported | +|----------|-----------| +| Direct VM connectivity in the VNet | Yes | +| Private endpoints in the VNet | Yes (with certain configurations) | +| VNet peering — remote VNet VMs | Yes (with ErGwScale 2+ units or updated ErGw3Az) | +| Load Balancer (Standard) in the VNet | Yes | +| VNet with Azure Firewall/NVA in-path via UDR | Limited — see Limitations | + +## Limitations + +FastPath has several important limitations to understand before enabling: + +### VNet Peering + +- FastPath with VNet peering is supported on **ErGwScale (2+ scale units)** and updated ErGw3Az gateways +- Older ErGw3Az deployments may not support FastPath to peered VNets +- Traffic to peered VNets may fall back to the gateway path if FastPath does not support the specific peering configuration + +### User-Defined Routes (UDRs) + +- **UDRs on the GatewaySubnet** are not evaluated by FastPath traffic (traffic bypasses the gateway) +- UDRs on **VM subnets** directing traffic to an NVA are respected for traffic originating from the VM, but inbound FastPath traffic may bypass the NVA +- If you need to force all inbound traffic through an NVA (e.g., Azure Firewall), FastPath may not be appropriate + +### Private Endpoints + +- FastPath to private endpoints is supported with **ErGwScale** or **ErGw3Az** in updated regions +- Check current documentation for the latest support status + +### DNS Private Resolver + +- FastPath traffic to DNS Private Resolver inbound endpoints follows the gateway path (not bypassed) + +### Basic Load Balancer + +- FastPath does not work with Basic Load Balancer — use Standard Load Balancer + +### Forced Tunneling + +- If the VNet has forced tunneling configured (default route 0.0.0.0/0 pointing to an NVA), FastPath bypasses the forced tunnel for inbound ExpressRoute traffic + +## Performance Comparison + +| Metric | Without FastPath | With FastPath | +|--------|-----------------|---------------| +| Latency (additional hop) | 1-2 ms (gateway hop) | ~0 ms (bypassed) | +| Throughput cap | Gateway SKU limit (1-40 Gbps) | Circuit bandwidth | +| Control plane | Through gateway | Through gateway | +| Data plane | Through gateway | Direct to VNet | + +### When FastPath Does NOT Improve Performance + +- If your bottleneck is the ExpressRoute circuit bandwidth (not the gateway) +- If traffic patterns are bursty and the gateway is not saturated +- If you need NVA/Firewall inspection of all inbound traffic (FastPath bypasses this) + +## Disabling FastPath + +```bash +az network vpn-connection update \ + --name \ + --resource-group \ + --express-route-gateway-bypass false +``` + +Traffic immediately falls back to the gateway path. No downtime expected but brief reconvergence may occur. + +## Troubleshooting + +### FastPath Enabled but No Performance Improvement + +1. **Verify gateway SKU** — must be ErGw3Az, Ultra Performance, or ErGwScale with 2+ units +2. **Check connection status** — connection must be `Connected` with `expressRouteGatewayBypass: true` +3. **VNet peering fallback** — if traffic targets a peered VNet, FastPath may not apply on older gateways +4. **UDR interference** — check if UDRs on VM subnets are redirecting traffic through an NVA +5. **Latency testing** — use Azure Network Watcher or `traceroute` to confirm the gateway hop is bypassed + +### Traffic Still Flowing Through Gateway + +1. **Control plane traffic always flows through the gateway** — only data-plane traffic is bypassed +2. **Unsupported scenarios** fall back to the gateway (e.g., Basic LB, DNS Private Resolver) +3. **Check feature registration** — some subscriptions may require feature flag registration for FastPath + +## Integration with Other Azure Services + +### FastPath and Azure Virtual WAN + +For vWAN, FastPath is not configured the same way. vWAN ExpressRoute gateways have their own FastPath behavior managed through the hub configuration (see azure-virtual-wan). + +### FastPath and VPN Coexistence + +When VPN and ER gateways coexist on the same VNet, FastPath applies only to ExpressRoute traffic. VPN traffic continues to flow through the VPN gateway normally. + +## Additional References + +- [About ExpressRoute FastPath](https://learn.microsoft.com/azure/expressroute/about-fastpath) +- [Configure FastPath](https://learn.microsoft.com/azure/expressroute/expressroute-howto-linkvnet-arm#configure-expressroute-fastpath) +- [ExpressRoute gateway SKUs](https://learn.microsoft.com/azure/expressroute/expressroute-about-virtual-network-gateways) diff --git a/plugin/skills/azure-expressroute/references/global-reach.md b/plugin/skills/azure-expressroute/references/global-reach.md new file mode 100644 index 000000000..31eef5fa8 --- /dev/null +++ b/plugin/skills/azure-expressroute/references/global-reach.md @@ -0,0 +1,173 @@ +# ExpressRoute Global Reach + +## Overview + +ExpressRoute Global Reach enables direct connectivity between two on-premises networks through the Microsoft global backbone, leveraging their existing ExpressRoute circuits. Traffic between the sites flows through the Microsoft network and never touches the public internet. + +``` +On-Premises Site A ──── ExpressRoute Circuit A ──── Microsoft Backbone ──── ExpressRoute Circuit B ──── On-Premises Site B +``` + +Without Global Reach, site-to-site connectivity between two ExpressRoute-connected locations would require a VPN tunnel over the internet or a router in an Azure VNet acting as transit — both suboptimal. + +## When to Use Global Reach + +- **Branch-to-branch traffic** through Azure backbone instead of over the internet +- **Data replication** between two on-premises datacenters using Microsoft's private backbone +- **Disaster recovery** between on-premises sites using low-latency private connectivity +- **Migration** between datacenters connected to different ExpressRoute peering locations + +## Requirements + +- Both circuits must have **private peering** configured and active +- Both circuits must be in **Provisioned** state +- At least one circuit must be **Premium** SKU (unless both are in the same geopolitical region) +- Peering locations must be in [supported regions](https://learn.microsoft.com/azure/expressroute/expressroute-global-reach#availability) +- Non-overlapping **/29 subnets** for the Global Reach BGP sessions (one for primary, can auto-allocate) + +## Supported Regions + +Global Reach is available in select peering locations. As of the current documentation, supported regions include: + +- **Americas:** Atlanta, Chicago, Dallas, Denver, Los Angeles, Miami, Minneapolis, New York, Phoenix, San Antonio, Seattle, Silicon Valley, Washington DC, Montreal, Toronto, Sao Paulo +- **Europe:** Amsterdam, Dublin, Frankfurt, Geneva, London, Madrid, Marseille, Milan, Oslo, Paris, Stockholm, Vienna, Zurich +- **Asia Pacific:** Hong Kong, Melbourne, Osaka, Perth, Seoul, Singapore, Sydney, Taipei, Tokyo +- **Middle East:** Dubai + +Check the [latest availability](https://learn.microsoft.com/azure/expressroute/expressroute-global-reach#availability) as new locations are added regularly. + +## Configuration + +### Enable Global Reach Between Two Circuits + +```bash +# Get the resource ID of Circuit B +CIRCUIT_B_ID=$(az network express-route show \ + --name \ + --resource-group \ + --query id -o tsv) + +# Enable Global Reach on Circuit A's private peering +az network express-route peering connection create \ + --circuit-name \ + --resource-group \ + --peering-name AzurePrivatePeering \ + --name \ + --peer-circuit $CIRCUIT_B_ID \ + --address-prefix 172.16.0.0/29 +``` + +The `--address-prefix` is a /29 subnet used for the BGP sessions between the two circuits. It must not overlap with any VNet, on-prem, or peering subnet. + +### Verify Global Reach Connection + +```bash +az network express-route peering connection show \ + --circuit-name \ + --resource-group \ + --peering-name AzurePrivatePeering \ + --name +``` + +**Expected output:** `circuitConnectionStatus: Connected` + +### Check Routes Learned via Global Reach + +```bash +# On Circuit A — should now show Site B's prefixes +az network express-route list-route-tables \ + --name \ + --resource-group \ + --path primary \ + --peering-name AzurePrivatePeering +``` + +You should see routes from Site B's on-premises network appearing in Circuit A's route table, and vice versa. + +## Architecture Considerations + +### Traffic Flow + +``` +Site A (10.1.0.0/16) → Circuit A → Azure Backbone → Circuit B → Site B (10.2.0.0/16) +``` + +- Traffic does **not** traverse any Azure VNet +- Traffic does **not** require any gateway or routing configuration in Azure VNets +- Both sites continue to reach Azure VNets through their respective circuit connections independently +- Global Reach adds site-to-site routes on top of existing Azure connectivity + +### Address Space Planning + +Ensure these ranges do not overlap: +- Site A on-premises prefixes +- Site B on-premises prefixes +- Azure VNet address spaces connected to either circuit +- Global Reach /29 peering subnet + +### Bandwidth + +Global Reach bandwidth is limited by the **lower bandwidth** of the two circuits. If Circuit A is 1 Gbps and Circuit B is 2 Gbps, the Global Reach connection is capped at 1 Gbps. + +### Latency + +Traffic follows the optimal path across the Microsoft backbone between the two peering locations. Latency depends on the physical distance between peering locations. For same-metro locations, latency is typically sub-millisecond. For cross-continent, expect tens of milliseconds. + +## Multi-Circuit Scenarios + +You can enable Global Reach between multiple circuit pairs to create a mesh: + +``` +Circuit A ←→ Circuit B (Global Reach) +Circuit A ←→ Circuit C (Global Reach) +Circuit B ←→ Circuit C (Global Reach) +``` + +This connects all three on-premises sites through the Microsoft backbone. + +## Removing Global Reach + +```bash +az network express-route peering connection delete \ + --circuit-name \ + --resource-group \ + --peering-name AzurePrivatePeering \ + --name +``` + +Removing Global Reach does not affect the circuits' connectivity to Azure VNets. + +## Troubleshooting + +### Connection Status Not "Connected" + +1. **Both circuits must be Provisioned** — check `serviceProviderProvisioningState` +2. **Private peering must be active** on both circuits +3. **Premium SKU required** if circuits are in different geopolitical regions +4. **Check region support** — not all peering locations support Global Reach +5. **Overlapping subnets** — the /29 Global Reach subnet must not conflict with existing ranges + +### Routes Not Propagating + +1. **Wait 5-10 minutes** after enabling — BGP convergence may take time +2. **Check on-prem advertisement** — your router must be advertising on-prem prefixes over private peering +3. **Route limits** — combined routes from both sites must be within the circuit's route limit (4,000 standard / 10,000 premium) +4. **Verify BGP sessions** — check that private peering BGP sessions are established on both circuits + +### Performance Issues + +1. **Bandwidth cap** — check the lower bandwidth of the two circuits +2. **Sub-optimal routing** — verify that traffic is taking the expected path through peering locations +3. **Asymmetric paths** — if using multiple circuits, ensure routing is symmetric to avoid drops + +## Cost Considerations + +- Global Reach has its own pricing, billed per GB of data transferred between the circuits +- Circuit costs are separate and still apply for each circuit +- No additional infrastructure (gateways, VMs) needed in Azure — cost is purely for the Global Reach data transfer + +## Additional References + +- [ExpressRoute Global Reach](https://learn.microsoft.com/azure/expressroute/expressroute-global-reach) +- [Configure Global Reach](https://learn.microsoft.com/azure/expressroute/expressroute-howto-set-global-reach-cli) +- [Global Reach availability](https://learn.microsoft.com/azure/expressroute/expressroute-global-reach#availability) diff --git a/plugin/skills/azure-expressroute/references/peering-config.md b/plugin/skills/azure-expressroute/references/peering-config.md new file mode 100644 index 000000000..036734593 --- /dev/null +++ b/plugin/skills/azure-expressroute/references/peering-config.md @@ -0,0 +1,220 @@ +# ExpressRoute Peering Configuration + +## Peering Types Overview + +ExpressRoute supports two peering types (Azure public peering is deprecated): + +| Peering | Purpose | What You Reach | BGP Required | +|---------|---------|---------------|-------------| +| **Azure Private Peering** | Connect to VNets | VMs, ILBs, private endpoints, all private IPs in linked VNets | Yes | +| **Microsoft Peering** | Connect to Microsoft services | Microsoft 365, Azure PaaS public IPs, Dynamics 365 | Yes | + +Both peerings can be configured on the same circuit simultaneously. + +## Azure Private Peering + +Private peering connects your on-premises network to Azure VNets over ExpressRoute. All traffic stays on the Microsoft backbone and never traverses the public internet. + +### Subnet Requirements + +You must provide two /30 subnets (or /126 for IPv6) — one for the primary link and one for the secondary link: + +- **Primary subnet:** /30 — first usable IP for your router, second usable IP for Microsoft router +- **Secondary subnet:** /30 — same pattern, for redundancy + +Example: +- Primary: 10.0.0.0/30 → Your router: 10.0.0.1, Microsoft: 10.0.0.2 +- Secondary: 10.0.0.4/30 → Your router: 10.0.0.5, Microsoft: 10.0.0.6 + +These subnets **must not overlap** with any VNet address space or on-prem ranges. + +### VLAN ID + +You must assign a unique VLAN ID for each peering. This VLAN isolates the peering traffic on the physical link. Coordinate with your provider for available VLAN IDs. + +### Create Private Peering + +```bash +az network express-route peering create \ + --circuit-name \ + --resource-group \ + --peering-type AzurePrivatePeering \ + --peer-asn \ + --primary-peer-subnet 10.0.0.0/30 \ + --secondary-peer-subnet 10.0.0.4/30 \ + --vlan-id 100 +``` + +### BGP Configuration for Private Peering + +| Parameter | Value | +|-----------|-------| +| Azure ASN | 12076 (always) | +| Your ASN | Any valid private ASN (e.g., 65001). Avoid: 12076, 65515, 65520 | +| Primary BGP session | Your router (10.0.0.1) ↔ Microsoft (10.0.0.2) | +| Secondary BGP session | Your router (10.0.0.5) ↔ Microsoft (10.0.0.6) | + +**Route advertisement:** Azure advertises all VNet address prefixes linked to the circuit. Your on-prem advertises your network prefixes. + +### Route Limits + +| SKU | Max Routes (Private Peering) | +|-----|------------------------------| +| Standard | 4,000 | +| Premium | 10,000 | + +If you exceed the route limit, the BGP session drops. Monitor route count: + +```bash +az network express-route peering show \ + --circuit-name \ + --resource-group \ + --name AzurePrivatePeering \ + --query "ipv4PeeringInfo.{advertisedRoutes:primaryPeerAddressPrefix, routeCount:routeCount}" +``` + +### MD5 Hash (Optional) + +For additional security, configure an MD5 hash on the BGP session: + +```bash +az network express-route peering update \ + --circuit-name \ + --resource-group \ + --name AzurePrivatePeering \ + --shared-key +``` + +## Microsoft Peering + +Microsoft peering provides access to Microsoft public services (Microsoft 365, Azure PaaS with public IPs, Dynamics 365) over the ExpressRoute circuit. + +### Requirements + +- **Public IP prefixes or ASN** — you must own the public IP prefixes you advertise, or use an ASN registered in an IRR (Internet Routing Registry) +- **Route filter mandatory** — you must create and attach a route filter specifying which BGP communities to accept +- **VLAN ID** — separate VLAN from private peering + +### Create Microsoft Peering + +```bash +az network express-route peering create \ + --circuit-name \ + --resource-group \ + --peering-type MicrosoftPeering \ + --peer-asn \ + --primary-peer-subnet \ + --secondary-peer-subnet \ + --vlan-id 200 \ + --advertised-public-prefixes +``` + +### Route Filters + +Route filters control which Microsoft service prefixes are advertised to your network. Without a route filter, no Microsoft service routes are received. + +```bash +# Create a route filter +az network route-filter create \ + --name \ + --resource-group + +# Add a rule to allow specific service communities +az network route-filter rule create \ + --filter-name \ + --resource-group \ + --name AllowAzureServices \ + --access Allow \ + --communities "12076:5010" "12076:5020" "12076:5030" + +# Attach route filter to Microsoft peering +az network express-route peering update \ + --circuit-name \ + --resource-group \ + --name MicrosoftPeering \ + --route-filter +``` + +### Common BGP Community Values + +| Community | Service | +|-----------|---------| +| 12076:5010 | Azure region services (e.g., Azure Storage, SQL) | +| 12076:5020 | Microsoft 365 (Exchange Online, SharePoint) | +| 12076:5030 | Other Microsoft online services | +| 12076:5040 | Azure region-specific (varies by region) | + +Region-specific communities allow you to receive routes only for specific Azure regions (e.g., 12076:51004 for West US). + +## Verifying Peering Status + +```bash +# Show all peerings on a circuit +az network express-route peering list \ + --circuit-name \ + --resource-group + +# Check BGP session state for private peering +az network express-route peering show \ + --circuit-name \ + --resource-group \ + --name AzurePrivatePeering \ + --query "{state:peeringState, primaryPeer:primaryPeerAddressPrefix, secondaryPeer:secondaryPeerAddressPrefix}" +``` + +### Expected States + +| State | Meaning | +|-------|---------| +| `Enabled` | Peering configured and active | +| `Disabled` | Peering configured but not active (check BGP) | +| Not present | Peering not yet configured | + +## Route Table Inspection + +View routes learned and advertised on the peering: + +```bash +# View routes advertised by Azure to your router (primary link) +az network express-route list-route-tables \ + --name \ + --resource-group \ + --path primary \ + --peering-name AzurePrivatePeering + +# View routes advertised by Azure to your router (secondary link) +az network express-route list-route-tables \ + --name \ + --resource-group \ + --path secondary \ + --peering-name AzurePrivatePeering +``` + +## Troubleshooting Peering Issues + +### BGP Session Not Establishing + +1. **Verify VLAN tagging** — ensure your router tags traffic with the correct VLAN ID +2. **Check subnet IPs** — your router must use the first usable IP; Microsoft uses the second +3. **Verify ASN** — your peer ASN must match what you configured; don't use 12076 +4. **Check MD5 hash** — if configured, both sides must use the same key +5. **Provider status** — circuit must be in `Provisioned` state before peering works + +### Routes Not Being Received + +1. **Route filter missing** (Microsoft peering) — attach a route filter with appropriate communities +2. **Route limit exceeded** — reduce the number of prefixes advertised from on-prem +3. **Circuit not linked to VNet** (private peering) — connect the ER gateway to the circuit +4. **Asymmetric routing** — verify both primary and secondary links are active and correctly configured + +### BGP Flapping + +1. **Check physical link stability** with your provider +2. **Verify hold timer** — default is 180 seconds; overly aggressive timers cause flapping +3. **Enable BFD** — Bidirectional Forwarding Detection provides faster failure detection without BGP timer sensitivity + +## Additional References + +- [Configure ExpressRoute peering](https://learn.microsoft.com/azure/expressroute/expressroute-howto-routing-arm) +- [Route filters for Microsoft peering](https://learn.microsoft.com/azure/expressroute/how-to-routefilter-cli) +- [BGP communities](https://learn.microsoft.com/azure/expressroute/expressroute-routing) diff --git a/plugin/skills/azure-firewall/SKILL.md b/plugin/skills/azure-firewall/SKILL.md new file mode 100644 index 000000000..5a84f23bb --- /dev/null +++ b/plugin/skills/azure-firewall/SKILL.md @@ -0,0 +1,160 @@ +--- +name: azure-firewall +description: "Manage Azure Firewall (Basic, Standard, Premium) and Azure Firewall Manager for centralized network filtering, DNAT/SNAT, threat intelligence, and IDPS. WHEN: azure firewall, firewall rules, firewall policy, DNAT rule, network rule, application rule, threat intelligence, IDPS, TLS inspection, forced tunneling firewall, firewall manager, central firewall management. DO NOT USE FOR: NSG rules on subnets/NICs (use azure-virtual-network), WAF for web apps (use azure-waf), DDoS protection (use azure-ddos-protection)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Firewall + +Azure Firewall is a managed, cloud-based network security service that protects Azure Virtual Network resources. It provides centralized network and application-level filtering with built-in high availability, unrestricted cloud scalability, and integration with Azure Monitor. Azure Firewall Manager enables centralized security policy management across multiple firewall instances. + +## When to Use This Skill + +- Deploying or configuring Azure Firewall in any SKU (Basic, Standard, Premium) +- Creating or modifying DNAT rules, network rules, or application rules +- Configuring rule collection groups and managing rule processing priority +- Setting up firewall policies or migrating from classic rules to firewall policies +- Enabling threat intelligence-based filtering on firewall traffic +- Configuring IDPS (Intrusion Detection and Prevention System) on Premium SKU +- Setting up TLS inspection for outbound or east-west traffic +- Configuring forced tunneling for Azure Firewall +- Managing multiple firewalls centrally with Azure Firewall Manager +- Setting up parent-child policy inheritance across hub-and-spoke topologies +- Diagnosing firewall connectivity issues, dropped traffic, or rule-matching problems + +## Rules + +1. Always confirm the Azure Firewall SKU before recommending features — IDPS and TLS inspection require Premium SKU. +2. Azure Firewall must be deployed in a dedicated subnet named **AzureFirewallSubnet** with a minimum /26 prefix. +3. Forced tunneling requires an additional **AzureFirewallManagementSubnet** (/26 minimum) with its own public IP. +4. Rule processing order is: DNAT rules → Network rules → Application rules. Within each type, rules are processed by priority (lowest number = highest priority). +5. Firewall policies are the recommended configuration method — classic rules are legacy and cannot coexist with policies on the same firewall. +6. When using Firewall Manager with secured virtual hubs, the firewall is deployed inside a Virtual WAN hub — this differs from a hub VNet deployment. +7. Always recommend diagnostic logging to Log Analytics for production firewalls — use Azure Firewall structured logs (Resource Specific) for better query performance. +8. SNAT is performed by default for traffic leaving to the internet. Configure private IP ranges if SNAT should not be applied to private-to-private traffic. +9. DNAT rules require an associated public IP and automatically create a corresponding network rule for return traffic. +10. Cross-reference with `azure-virtual-network` for UDR configuration that routes traffic through the firewall, and with `azure-waf` when the user also needs Layer 7 web app protection. + +## MCP Tools + +| Tool | Resource | Use | +|------|----------|-----| +| `azure__network` | `firewall_list` | List all Azure Firewall instances in a subscription or resource group | +| `azure__network` | `firewall_get` | Get configuration details for a specific Azure Firewall instance | +| `azure__network` | `firewall_policy_list` | List all firewall policies in a subscription or resource group | + +## CLI Fallback + +```bash +# List all firewalls in a resource group +az network firewall list --resource-group -o table + +# Get firewall details +az network firewall show --name --resource-group + +# Create a firewall policy +az network firewall policy create \ + --name \ + --resource-group \ + --sku Premium \ + --threat-intel-mode Alert + +# Create a rule collection group +az network firewall policy rule-collection-group create \ + --name \ + --policy-name \ + --resource-group \ + --priority 200 + +# Add a network rule collection +az network firewall policy rule-collection-group collection add-filter-collection \ + --name "allow-dns" \ + --policy-name \ + --resource-group \ + --rule-collection-group-name \ + --collection-priority 100 \ + --action Allow \ + --rule-name "dns-rule" \ + --rule-type NetworkRule \ + --source-addresses "10.0.0.0/16" \ + --destination-addresses "168.63.129.16" \ + --destination-ports 53 \ + --ip-protocols UDP TCP + +# Add a DNAT rule collection +az network firewall policy rule-collection-group collection add-nat-collection \ + --name "inbound-rdp" \ + --policy-name \ + --resource-group \ + --rule-collection-group-name \ + --collection-priority 100 \ + --action DNAT \ + --rule-name "rdp-to-vm" \ + --rule-type NatRule \ + --source-addresses "*" \ + --destination-addresses \ + --destination-ports 3389 \ + --ip-protocols TCP \ + --translated-address 10.0.1.4 \ + --translated-port 3389 + +# Add an application rule collection +az network firewall policy rule-collection-group collection add-filter-collection \ + --name "allow-web" \ + --policy-name \ + --resource-group \ + --rule-collection-group-name \ + --collection-priority 200 \ + --action Allow \ + --rule-name "allow-microsoft" \ + --rule-type ApplicationRule \ + --source-addresses "10.0.0.0/16" \ + --protocols Https=443 \ + --target-fqdns "*.microsoft.com" + +# Enable diagnostic logging +az monitor diagnostic-settings create \ + --name "fw-diag" \ + --resource \ + --workspace \ + --logs '[{"categoryGroup":"allLogs","enabled":true}]' \ + --metrics '[{"category":"AllMetrics","enabled":true}]' + +# List firewall policies +az network firewall policy list --resource-group -o table + +# Update threat intelligence mode +az network firewall policy update \ + --name \ + --resource-group \ + --threat-intel-mode Deny +``` + +## Key Concepts + +- **SKU tiers**: Basic (small workloads, limited features), Standard (L3–L7 filtering, threat intelligence, DNS proxy), Premium (IDPS, TLS inspection, URL filtering, web categories) +- **AzureFirewallSubnet**: Dedicated /26+ subnet required; must not have any other resources +- **Firewall policies**: Recommended over classic rules; support hierarchy (parent-child) and can be managed via Azure Firewall Manager +- **Rule collection groups**: Containers for rule collections; processed by priority; organize rules logically (e.g., by team or application) +- **Rule processing**: DNAT → Network → Application; within each type, lowest priority number is processed first; first match wins (except Application rules which are "allow" collections) +- **Threat intelligence**: Alerts on or denies traffic from/to known malicious IPs and domains; powered by the Microsoft Threat Intelligence feed +- **IDPS**: Signature-based intrusion detection and prevention (Premium only); supports Alert and Alert+Deny modes +- **TLS inspection**: Decrypts and inspects outbound HTTPS traffic (Premium only); requires an intermediate CA certificate in Key Vault +- **DNS proxy**: Azure Firewall acts as a DNS proxy so FQDN-based rules resolve correctly; must be enabled for FQDN filtering in network rules +- **Forced tunneling**: Routes all internet-bound traffic to an on-premises appliance; requires a management subnet and management public IP +- **Azure Firewall Manager**: Centrally manages firewall policies and route configurations across hubs; supports both VNet hubs and Virtual WAN secured hubs +- **Throughput**: Standard up to 30 Gbps, Premium up to 100 Gbps; use multiple public IPs to scale SNAT ports (2,496 ports per IP) + +## References + +- [firewall-skus.md](references/firewall-skus.md) — SKU comparison (Basic, Standard, Premium) +- [rule-types.md](references/rule-types.md) — DNAT, network, and application rules; rule processing order +- [firewall-policy.md](references/firewall-policy.md) — Firewall policies, hierarchy, and Azure Firewall Manager +- [forced-tunneling.md](references/forced-tunneling.md) — Forced tunneling configuration +- [idps.md](references/idps.md) — IDPS, TLS inspection, and Premium features +- [Azure Firewall documentation](https://learn.microsoft.com/azure/firewall/overview) +- [Azure Firewall Manager documentation](https://learn.microsoft.com/azure/firewall-manager/overview) +- [Azure Firewall pricing](https://azure.microsoft.com/pricing/details/azure-firewall/) diff --git a/plugin/skills/azure-firewall/references/firewall-policy.md b/plugin/skills/azure-firewall/references/firewall-policy.md new file mode 100644 index 000000000..e3748cf99 --- /dev/null +++ b/plugin/skills/azure-firewall/references/firewall-policy.md @@ -0,0 +1,187 @@ +# Azure Firewall Policies and Firewall Manager + +Firewall policies are the recommended way to configure Azure Firewall rules. They replace the legacy "classic rules" model with a structured, reusable, and hierarchical configuration framework. Azure Firewall Manager provides centralized management of firewall policies across multiple firewalls and hubs. + +## Firewall Policy vs Classic Rules + +| Aspect | Firewall Policy | Classic Rules | +|--------|----------------|---------------| +| Management model | Azure Resource Manager resource | Firewall-embedded configuration | +| Reusability | One policy can be shared across firewalls | Each firewall has its own rules | +| Hierarchy | Parent-child inheritance | None | +| Firewall Manager support | Yes | No | +| Rule organization | Rule collection groups → collections → rules | Rule collections → rules | +| Recommended | **Yes** | No (legacy) | +| Coexistence | Cannot coexist with classic rules on same firewall | Cannot coexist with policies | + +**Migration**: Classic rules can be migrated to firewall policies using the Azure portal migration wizard or the `AzureFirewallMigration.ps1` PowerShell script. After migration, the firewall switches to policy mode and classic rules are removed. + +## Firewall Policy Structure + +``` +Firewall Policy (parent or standalone) + │ + ├── Rule Collection Group "infra-rcg" (priority: 100) + │ ├── Network Rule Collection "dns-rules" (priority: 100, action: Allow) + │ │ ├── Rule: allow-azure-dns + │ │ └── Rule: allow-custom-dns + │ └── Application Rule Collection "azure-services" (priority: 200, action: Allow) + │ ├── Rule: allow-windows-update + │ └── Rule: allow-azure-monitor + │ + ├── Rule Collection Group "app-team-a" (priority: 200) + │ ├── Network Rule Collection "backend-rules" (priority: 100, action: Allow) + │ └── Application Rule Collection "web-access" (priority: 200, action: Allow) + │ + └── Rule Collection Group "deny-all" (priority: 999) + └── Network Rule Collection "explicit-deny" (priority: 100, action: Deny) + └── Rule: deny-everything +``` + +### Limits + +| Resource | Limit | +|----------|-------| +| Rule collection groups per policy | 90 (default, increasable) | +| Rule collections per group | No hard limit (within total rule count) | +| Total rules per policy | 10,000 (default, increasable to 20,000) | +| Unique source/destination per rule | 250 | +| IP Groups per policy | 200 | +| IPs across all IP Groups | 5,000 | +| Policies per firewall | 1 | +| Firewalls per policy | Unlimited | + +## Parent-Child Policy Inheritance + +Firewall policies support a hierarchy model where a child policy inherits all rules from its parent and can add its own rules. + +### How inheritance works + +``` +Parent Policy (Base rules — managed by central security team) + ├── Infra rules (DNS, NTP, monitoring) + ├── Threat intelligence: Deny + └── IDPS mode: Alert+Deny + │ + ├── Child Policy A (Hub-East — managed by app team A) + │ ├── Inherits all parent rules (read-only) + │ └── Adds app-specific rules + │ + └── Child Policy B (Hub-West — managed by app team B) + ├── Inherits all parent rules (read-only) + └── Adds app-specific rules +``` + +### Inheritance rules +- Child policies inherit **all** rule collection groups from the parent +- Inherited rules are read-only in the child — they cannot be modified or deleted at the child level +- Child policies can add new rule collection groups with priorities that do not conflict with parent groups +- Parent rules are always processed first (parent rule collection groups have effective priority over child groups regardless of numeric priority) +- Threat intelligence mode and IDPS settings are inherited but can be overridden in the child to be **more** restrictive (not less) +- DNS settings, TLS inspection configuration, and IDPS bypass lists are inherited + +### CLI: Create parent-child hierarchy + +```bash +# Create parent policy +az network firewall policy create \ + --name "base-policy" \ + --resource-group \ + --sku Premium \ + --threat-intel-mode Deny + +# Create child policy inheriting from parent +az network firewall policy create \ + --name "hub-east-policy" \ + --resource-group \ + --sku Premium \ + --base-policy + +# Associate child policy with a firewall +az network firewall update \ + --name \ + --resource-group \ + --firewall-policy +``` + +## Azure Firewall Manager + +Azure Firewall Manager is a centralized security management service that provides policy and route management for cloud-based security perimeters. + +### Capabilities + +- **Central policy management**: Create and manage firewall policies and associate them across multiple firewalls +- **Hub management**: Deploy and manage firewalls in VNet hubs (hub VNet) or Virtual WAN hubs (secured virtual hub) +- **Route management**: Configure route intent and route policies for secured virtual hubs +- **Third-party SECaaS**: Integrate third-party security-as-a-service providers alongside Azure Firewall +- **Policy analytics**: View top hit rules, flow trends, and rule optimization suggestions + +### Deployment models + +#### Hub VNet (traditional hub-and-spoke) +- Firewall deployed in a standard VNet with AzureFirewallSubnet +- UDRs on spoke subnets route traffic through the firewall private IP +- Managed by Firewall Manager policies but routing is manual (UDRs) + +#### Secured Virtual Hub (Virtual WAN) +- Firewall deployed inside a Virtual WAN hub +- Routing is managed by Virtual WAN routing intent — no manual UDRs needed +- Firewall Manager configures both the firewall and routing policies together +- Supports inter-hub routing through the Azure backbone + +### CLI: Manage policies via Firewall Manager + +```bash +# List all firewall policies (Firewall Manager scope) +az network firewall policy list -o table + +# List all firewalls and their associated policies +az network firewall list --query "[].{Name:name, Policy:firewallPolicy.id}" -o table + +# Update threat intelligence on a policy (propagates to all associated firewalls) +az network firewall policy update \ + --name \ + --resource-group \ + --threat-intel-mode Deny + +# Move a firewall to a different policy +az network firewall update \ + --name \ + --resource-group \ + --firewall-policy +``` + +## Best Practices + +1. **Use a parent policy for org-wide baselines**: DNS settings, threat intelligence, IDPS mode, and infrastructure allow rules should be in the parent. Let teams manage their own child policies for app-specific rules. + +2. **One policy per hub firewall**: Even though one policy can serve multiple firewalls, keep hub-specific rules in separate child policies for isolation and team autonomy. + +3. **Reserve priority bands**: Establish priority conventions across the organization: + - 100–199: Infrastructure (DNS, NTP, monitoring agents) + - 200–499: Application rules (per-team or per-app groups) + - 500–799: Shared services + - 800–999: Explicit deny / catch-all rules + +4. **Use IP Groups for large IP sets**: Instead of listing IPs in individual rules, create IP Groups and reference them. Changes to the IP Group propagate to all rules. + +5. **Enable policy analytics**: Use Firewall Manager's analytics to identify unused rules, top flows, and optimization opportunities. + +6. **Version control policies**: Export firewall policies as ARM/Bicep templates and manage them in source control. Use CI/CD pipelines for policy deployment. + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Child policy cannot override parent rules | By design — inheritance is additive | Add new rules in child; to change parent behavior, modify the parent policy | +| Policy association fails | SKU mismatch | Child policy SKU must match parent; firewall SKU must match policy SKU | +| Rule limit reached | Exceeded 10,000 rules | Request a limit increase or consolidate rules using IP Groups and service tags | +| Changes not taking effect | Firewall still using old policy | Verify policy is associated; check `az network firewall show` for policy ID | +| Cannot delete parent policy | Child policies still reference it | Delete or reassign all child policies first | + +## Related + +- [rule-types.md](rule-types.md) — Rule types and processing order +- [firewall-skus.md](firewall-skus.md) — SKU requirements for policy features +- [Azure Firewall Manager documentation](https://learn.microsoft.com/azure/firewall-manager/overview) +- [Firewall policy rule sets](https://learn.microsoft.com/azure/firewall/policy-rule-sets) diff --git a/plugin/skills/azure-firewall/references/firewall-skus.md b/plugin/skills/azure-firewall/references/firewall-skus.md new file mode 100644 index 000000000..ef5ee829e --- /dev/null +++ b/plugin/skills/azure-firewall/references/firewall-skus.md @@ -0,0 +1,108 @@ +# Azure Firewall SKU Comparison + +Azure Firewall offers three SKUs — Basic, Standard, and Premium — each targeting different workload profiles. Choosing the right SKU at deployment is critical because upgrading from Basic to Standard requires redeployment; Standard to Premium can be upgraded in-place. + +## Feature Matrix + +| Feature | Basic | Standard | Premium | +|---------|-------|----------|---------| +| **Intended workload** | SMB, low-throughput | Most production workloads | High-security, regulated | +| **Throughput** | Up to 250 Mbps | Up to 30 Gbps | Up to 100 Gbps | +| **Availability Zones** | No | Yes | Yes | +| **DNAT rules** | Limited | Yes | Yes | +| **Network rules (L3/L4)** | Yes | Yes | Yes | +| **Application rules (L7)** | Yes (FQDN-based) | Yes | Yes | +| **Threat intelligence** | Alert only | Alert or Deny | Alert or Deny | +| **DNS proxy** | No | Yes | Yes | +| **FQDN in network rules** | No | Yes (requires DNS proxy) | Yes | +| **FQDN tags** | No | Yes | Yes | +| **Web categories** | No | No | Yes | +| **URL filtering** | No | No | Yes (full path) | +| **IDPS** | No | No | Yes | +| **TLS inspection** | No | No | Yes | +| **Explicit proxy** | No | No | Yes | +| **Forced tunneling** | No | Yes | Yes | +| **Multiple public IPs** | No | Yes (up to 250) | Yes (up to 250) | +| **Firewall policy** | Yes | Yes | Yes | +| **Azure Firewall Manager** | Yes | Yes | Yes | +| **Structured logs** | Yes | Yes | Yes | +| **Active FTP support** | No | Yes | Yes | + +## SKU Selection Guidance + +### Choose Basic when + +- Throughput needs are under 250 Mbps +- You need basic L3/L4 filtering with FQDN-based application rules +- Budget is constrained and advanced features are not required +- The workload is dev/test or a small branch office +- You do not need availability zones + +### Choose Standard when + +- Production workloads requiring high availability and zone redundancy +- You need threat intelligence-based filtering (alert and deny) +- DNS proxy functionality is required for FQDN filtering in network rules +- Throughput up to 30 Gbps is sufficient +- You need DNAT for inbound traffic or multiple public IPs for SNAT scaling +- Forced tunneling to an on-premises appliance is required + +### Choose Premium when + +- Regulatory or compliance requirements demand TLS inspection of outbound traffic +- IDPS (Intrusion Detection and Prevention) is required for deep packet inspection +- URL-level filtering (beyond FQDN) is needed — e.g., block `example.com/admin` but allow `example.com` +- Web category filtering is required (e.g., block gambling, social media) +- The workload handles sensitive data and needs the highest throughput (up to 100 Gbps) +- You are operating in industries like finance, healthcare, or government + +## Throughput and Scaling + +- **Basic**: Fixed at ~250 Mbps; no autoscaling +- **Standard**: Baseline ~30 Gbps; can burst higher with autoscaling; scales based on CPU and throughput +- **Premium**: Baseline ~100 Gbps with TLS inspection disabled; TLS inspection reduces effective throughput + +Scaling considerations: +- Azure Firewall autoscales within its SKU limits based on load +- Use multiple public IPs to increase SNAT port capacity: 2,496 ports per public IP per backend instance +- For maximum SNAT capacity, deploy with up to 250 public IPs +- Monitor the `FirewallHealth`, `Throughput`, and `SNATPortUtilization` metrics + +## Pricing Tiers + +All SKUs have two cost components: +1. **Deployment (fixed hourly)**: Charged per firewall instance per hour +2. **Data processing**: Charged per GB processed by the firewall + +Approximate monthly costs (check [Azure pricing](https://azure.microsoft.com/pricing/details/azure-firewall/) for current rates): +- **Basic**: ~$0.395/hr deployment + $0.065/GB data processed +- **Standard**: ~$1.25/hr deployment + $0.016/GB data processed +- **Premium**: ~$1.75/hr deployment + $0.016/GB data processed + +Cost optimization tips: +- Use Azure Firewall Manager to share a single firewall policy across multiple firewalls +- For dev/test environments, consider stopping the firewall during off-hours with `az network firewall update --name --resource-group --no-wait` and deallocating IPs +- Basic SKU has higher per-GB cost — cross the cost-efficiency threshold at moderate traffic volumes where Standard becomes cheaper + +## Upgrade Paths + +- **Basic → Standard**: Requires delete and redeploy (no in-place upgrade) +- **Standard → Premium**: In-place upgrade supported via `az network firewall update --sku AZFW_Hub --tier Premium` or through the portal +- **Premium → Standard**: Downgrade is not supported; must redeploy +- When upgrading to Premium, ensure a Key Vault-managed intermediate CA certificate is provisioned if TLS inspection will be used + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Low throughput | SKU limits hit | Check `Throughput` metric; consider upgrading SKU | +| SNAT port exhaustion | Not enough public IPs | Add public IPs; monitor `SNATPortUtilization` | +| Feature unavailable | Wrong SKU | Verify SKU supports the feature (see matrix above) | +| Cannot enable IDPS | Running Standard SKU | Upgrade to Premium | +| Cannot enable forced tunneling | Running Basic SKU | Deploy Standard or Premium | + +## Related + +- [rule-types.md](rule-types.md) — How rule processing works across all SKUs +- [idps.md](idps.md) — Premium-only IDPS and TLS inspection details +- [Azure Firewall SKU documentation](https://learn.microsoft.com/azure/firewall/choose-firewall-sku) diff --git a/plugin/skills/azure-firewall/references/forced-tunneling.md b/plugin/skills/azure-firewall/references/forced-tunneling.md new file mode 100644 index 000000000..15fac9edd --- /dev/null +++ b/plugin/skills/azure-firewall/references/forced-tunneling.md @@ -0,0 +1,172 @@ +# Azure Firewall Forced Tunneling + +Forced tunneling allows you to route all internet-bound traffic from Azure Firewall to an on-premises network or network virtual appliance (NVA) for inspection before it reaches the internet. This is a common requirement for organizations with regulatory mandates that all internet egress pass through an on-premises proxy or security stack. + +## How Forced Tunneling Works + +In normal operation, Azure Firewall sends internet-bound traffic directly to the internet. With forced tunneling: + +1. A default route (0.0.0.0/0) on the AzureFirewallSubnet points to an on-premises gateway (VPN/ExpressRoute) or NVA +2. Internet-bound traffic from the firewall is routed to the on-premises network instead of directly to the internet +3. The on-premises appliance inspects, logs, or modifies the traffic before forwarding it to the internet (or dropping it) +4. The firewall retains a **separate management path** via the AzureFirewallManagementSubnet for its own control-plane traffic (health probes, updates, metrics) + +``` +Spoke VNet traffic + │ + ▼ +┌──────────────┐ Data plane traffic ┌────────────────┐ +│ Azure │ ───(0.0.0.0/0 UDR)───→ │ On-premises / │ ──→ Internet +│ Firewall │ │ NVA │ +│ │ Management traffic └────────────────┘ +│ │ ───(mgmt public IP)───→ Internet (direct) +└──────────────┘ +``` + +## Prerequisites + +Forced tunneling requires: + +1. **Azure Firewall Standard or Premium** — forced tunneling is not supported on Basic SKU +2. **AzureFirewallManagementSubnet** — a dedicated /26 (minimum) subnet in the same VNet +3. **Management public IP** — a separate public IP address associated with the management subnet +4. **UDR on AzureFirewallSubnet** — default route (0.0.0.0/0) pointing to the on-premises gateway or NVA next hop + +## Subnet Requirements + +| Subnet | Name (exact) | Minimum size | Purpose | +|--------|-------------|--------------|---------| +| Firewall subnet | `AzureFirewallSubnet` | /26 | Handles data-plane traffic (user workload traffic) | +| Management subnet | `AzureFirewallManagementSubnet` | /26 | Handles management-plane traffic (health probes, updates, telemetry) | + +**Important**: +- The management subnet name must be exactly `AzureFirewallManagementSubnet` — Azure will not accept any other name +- The management subnet must have a direct internet path via its public IP (no UDR overriding 0.0.0.0/0 on this subnet) +- The management subnet must not have an NSG that blocks outbound internet access +- Do not apply a UDR with a 0.0.0.0/0 route to the management subnet + +## Configuration Steps + +### Step 1: Create the management subnet + +```bash +az network vnet subnet create \ + --name AzureFirewallManagementSubnet \ + --resource-group \ + --vnet-name \ + --address-prefix 10.0.2.0/26 +``` + +### Step 2: Create the management public IP + +```bash +az network public-ip create \ + --name \ + --resource-group \ + --sku Standard \ + --allocation-method Static +``` + +### Step 3: Deploy the firewall with forced tunneling + +When deploying a new firewall with forced tunneling: + +```bash +az network firewall create \ + --name \ + --resource-group \ + --vnet-name \ + --conf-name data-ip-config \ + --public-ip \ + --m-conf-name mgmt-ip-config \ + --m-public-ip +``` + +The `--m-conf-name` and `--m-public-ip` parameters enable the management interface for forced tunneling. + +### Step 4: Create a UDR for the AzureFirewallSubnet + +```bash +# Create route table +az network route-table create \ + --name \ + --resource-group + +# Add default route pointing to on-premises/NVA +az network route-table route create \ + --name "forced-tunnel-default" \ + --resource-group \ + --route-table-name \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address + +# Associate route table with AzureFirewallSubnet +az network vnet subnet update \ + --name AzureFirewallSubnet \ + --resource-group \ + --vnet-name \ + --route-table +``` + +### For VPN/ExpressRoute gateway as next hop + +If using a VPN Gateway or ExpressRoute gateway for the tunnel (instead of an NVA IP): + +```bash +az network route-table route create \ + --name "forced-tunnel-default" \ + --resource-group \ + --route-table-name \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type VirtualNetworkGateway +``` + +## Important Behavior Notes + +1. **DNAT is not supported**: When forced tunneling is enabled, DNAT rules cannot be used because the inbound return path would be asymmetric. Remove or avoid DNAT rules on forced-tunneled firewalls. + +2. **Public IP on the data plane**: The firewall still has a public IP on the data subnet even with forced tunneling. This IP is used for SNAT of private-to-private traffic and Azure service dependencies. It does NOT receive inbound traffic from the internet when forced tunneling is active. + +3. **DNS considerations**: If DNS queries also route through the tunnel, ensure the on-premises DNS infrastructure can resolve Azure-specific FQDNs needed by firewall application rules. + +4. **Firewall health**: If the management subnet loses internet connectivity, the firewall becomes unhealthy. Always ensure the management path has uninterrupted internet access. + +5. **Asymmetric routing prevention**: Traffic from spokes to the internet must flow: Spoke → Firewall → On-prem → Internet, and the return path must follow the same reverse path. Asymmetric routing will cause dropped connections. + +## Adding Forced Tunneling to an Existing Firewall + +An existing firewall without forced tunneling cannot have forced tunneling added in-place. You must: + +1. Stop (deallocate) the existing firewall +2. Create the management subnet and management public IP +3. Reallocate the firewall with the management IP configuration +4. Create the UDR on the AzureFirewallSubnet + +```bash +# Deallocate the firewall +az network firewall ip-config delete \ + --firewall-name \ + --resource-group \ + --name + +# Reallocate with management interface (portal or ARM template recommended) +``` + +> **Tip**: For existing firewalls, it is often easier to use an ARM/Bicep template or the Azure portal to reconfigure with forced tunneling than CLI. + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Firewall shows unhealthy | Management subnet has a UDR overriding 0.0.0.0/0 | Remove UDR from AzureFirewallManagementSubnet; it must have direct internet access | +| Firewall shows unhealthy | NSG blocking outbound on management subnet | Remove or adjust NSG to allow outbound internet | +| Internet traffic not reaching on-prem | UDR not applied to AzureFirewallSubnet | Verify route table association on the correct subnet | +| DNAT rules not working | Expected — DNAT is incompatible with forced tunneling | Use a load balancer or different ingress method | +| Spoke traffic bypasses firewall | Spoke UDRs not pointing to firewall | Ensure spoke subnets have UDR 0.0.0.0/0 → firewall private IP | + +## Related + +- [firewall-skus.md](firewall-skus.md) — SKU support for forced tunneling (Standard and Premium only) +- [rule-types.md](rule-types.md) — DNAT incompatibility with forced tunneling +- [Azure Firewall forced tunneling](https://learn.microsoft.com/azure/firewall/forced-tunneling) diff --git a/plugin/skills/azure-firewall/references/idps.md b/plugin/skills/azure-firewall/references/idps.md new file mode 100644 index 000000000..fdce4ed44 --- /dev/null +++ b/plugin/skills/azure-firewall/references/idps.md @@ -0,0 +1,235 @@ +# Azure Firewall IDPS and TLS Inspection + +IDPS (Intrusion Detection and Prevention System) and TLS inspection are Premium-only features that provide deep packet inspection and encrypted traffic analysis. Together, they enable Azure Firewall to detect and block sophisticated threats hiding in encrypted traffic. + +## IDPS Overview + +IDPS uses signature-based detection to identify known threats in network traffic. It inspects packet payloads — not just headers — to find malware, exploits, command-and-control traffic, and other malicious activity. + +### Modes + +| Mode | Behavior | +|------|----------| +| **Off** | IDPS disabled | +| **Alert** | Generates alerts for matched signatures; does not block traffic | +| **Alert and Deny** | Generates alerts AND blocks traffic matching signatures | + +### Key characteristics +- Over 67,000 rules across 50+ categories (malware, exploits, phishing, crypto mining, etc.) +- Signatures are updated automatically by Microsoft (multiple times per day) +- Covers all ports and protocols (not just HTTP/HTTPS) +- Inspects east-west and north-south traffic +- Without TLS inspection, IDPS can only inspect unencrypted traffic and TLS handshake metadata (SNI, certificate info) + +### Configure IDPS via CLI + +```bash +# Enable IDPS in Alert+Deny mode +az network firewall policy update \ + --name \ + --resource-group \ + --idps-mode Alert + +# To set Alert and Deny mode, use ARM/Bicep or portal +# CLI currently supports Alert and Off; use this ARM property: +# "intrusionDetection": { "mode": "Deny" } +``` + +### IDPS Signature Management + +You can customize IDPS behavior per signature: + +```bash +# Override a specific signature to Alert only (even if mode is Deny) +az network firewall policy intrusion-detection add \ + --policy-name \ + --resource-group \ + --mode Alert \ + --signature-id 2024897 +``` + +**Bypass list**: Exclude specific traffic from IDPS inspection: + +```bash +# Bypass IDPS for traffic from a specific source to destination +az network firewall policy intrusion-detection add \ + --policy-name \ + --resource-group \ + --mode Off \ + --source-addresses "10.0.1.0/24" \ + --destination-addresses "10.0.2.0/24" \ + --destination-ports "443" \ + --protocol TCP +``` + +### Signature categories + +| Category | Examples | +|----------|----------| +| Malware | Trojan, ransomware, worm signatures | +| Exploit | Known CVE exploits, buffer overflows | +| Command and Control | C2 beacons, DNS tunneling, IRC-based C2 | +| Phishing | Known phishing domains and patterns | +| Crypto Mining | Mining pool connections, Stratum protocol | +| DoS | Application-layer DoS patterns | +| Scan | Port scanning, vulnerability scanning | +| Policy | Tor exit nodes, anonymizer traffic | + +## TLS Inspection + +TLS inspection (also called SSL inspection or SSL decryption) allows Azure Firewall to decrypt outbound and east-west HTTPS traffic, inspect the plaintext content with IDPS and application rules, and re-encrypt it before forwarding. + +### Why TLS inspection matters +- Over 90% of web traffic is encrypted — without TLS inspection, IDPS and application rules can only see: + - TLS handshake metadata (SNI hostname, certificate details) + - IP addresses and ports +- With TLS inspection, the firewall can: + - Apply IDPS signatures to decrypted payloads + - Perform URL-level filtering (e.g., block `example.com/malware` while allowing `example.com`) + - Inspect web categories at the URL level + - Detect malware in HTTPS downloads + +### How it works + +``` +Client Azure Firewall Target Server + │ │ │ + │──TLS handshake─────────→│ │ + │ (FW presents its cert) │──TLS handshake──────────────→│ + │←─────────TLS established│←──────────TLS established────│ + │ │ │ + │──Encrypted request─────→│ Decrypt → Inspect → Re-encrypt│ + │ │──Encrypted request──────────→│ + │←─Encrypted response─────│ Decrypt ← Inspect ← Re-encrypt│ + │ │←──Encrypted response─────────│ +``` + +Azure Firewall acts as a TLS termination proxy: +1. The client connects to the firewall thinking it is the destination +2. The firewall presents its own CA-signed certificate to the client +3. The firewall opens a separate TLS connection to the actual destination +4. Traffic is decrypted, inspected, and re-encrypted in both directions + +### Certificate Requirements + +TLS inspection requires an **intermediate CA certificate** stored in Azure Key Vault: + +| Requirement | Detail | +|-------------|--------| +| Certificate type | Intermediate CA (not self-signed root, not leaf) | +| Key type | RSA 4096-bit recommended | +| Storage | Azure Key Vault with firewall managed identity access | +| Validity | Should have a long validity period (certificates are used to sign session certs) | +| Trust chain | The root CA that signed the intermediate cert must be trusted by all clients | + +### Setup steps + +#### Step 1: Create or obtain an intermediate CA certificate + +For testing, you can generate a self-signed root and intermediate: +```bash +# (Use OpenSSL or your organization's PKI to create an intermediate CA) +# The intermediate CA cert and private key must be uploaded to Key Vault as a PFX +``` + +For production, use your organization's internal PKI to issue an intermediate CA certificate. + +#### Step 2: Upload to Azure Key Vault + +```bash +# Create Key Vault (if not existing) +az keyvault create \ + --name \ + --resource-group \ + --location + +# Import the intermediate CA certificate (PFX format) +az keyvault certificate import \ + --vault-name \ + --name "fw-intermediate-ca" \ + --file \ + --password +``` + +#### Step 3: Configure firewall managed identity and Key Vault access + +```bash +# Create a user-assigned managed identity +az identity create \ + --name \ + --resource-group + +# Grant the identity access to Key Vault secrets and certificates +az keyvault set-policy \ + --name \ + --object-id \ + --secret-permissions get list \ + --certificate-permissions get list + +# Associate managed identity with the firewall policy +az network firewall policy update \ + --name \ + --resource-group \ + --identity-type UserAssigned \ + --user-assigned-identity +``` + +#### Step 4: Enable TLS inspection on the firewall policy + +```bash +# Configure TLS inspection with the Key Vault certificate +az network firewall policy update \ + --name \ + --resource-group \ + --key-vault-secret-id +``` + +#### Step 5: Enable TLS inspection on application rules + +TLS inspection is not enabled globally — it must be enabled per application rule collection: + +In the rule collection, set `terminateTLS: true` for the application rule collection that should inspect encrypted traffic. + +### Client trust + +For TLS inspection to work without certificate errors: +- The root CA that signed the intermediate certificate must be in the client's trusted root certificate store +- For domain-joined machines, use Group Policy to distribute the root CA +- For non-domain machines, manually install the root CA or use MDM +- For Linux/macOS, add the root CA to the system trust store + +## Premium Feature Summary + +| Feature | Description | Requires TLS Inspection | +|---------|-------------|------------------------| +| IDPS (unencrypted) | Inspect cleartext traffic for known threats | No | +| IDPS (encrypted) | Inspect decrypted HTTPS traffic for threats | Yes | +| URL filtering | Filter by full URL path, not just FQDN | Yes | +| Web categories | Categorize and filter traffic by content type | Partial (SNI-based without TLS, URL-based with TLS) | +| Explicit proxy | Firewall acts as an explicit HTTP/HTTPS proxy | No | + +## Performance Considerations + +- TLS inspection adds latency (decrypt + re-encrypt per connection) +- IDPS reduces effective throughput — Premium can handle up to 100 Gbps without TLS inspection; with TLS inspection enabled, expect lower throughput +- Signature evaluation is parallelized but CPU-intensive for high-connection-rate workloads +- Use bypass rules to exclude trusted, high-volume traffic from IDPS/TLS (e.g., trusted Azure service endpoints) + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Certificate errors on clients | Root CA not trusted | Distribute the root CA to client trust stores | +| TLS inspection not decrypting | Rule collection not configured for TLS termination | Set `terminateTLS: true` on application rule collection | +| IDPS not detecting encrypted threats | TLS inspection not enabled | Enable TLS inspection for application rules | +| Key Vault access denied | Managed identity missing permissions | Verify identity has get/list on secrets and certificates | +| Performance degradation | TLS inspection overhead | Bypass IDPS/TLS for trusted high-volume services | +| IDPS false positive | Signature triggered on legitimate traffic | Override specific signature to Alert mode or add to bypass list | + +## Related + +- [firewall-skus.md](firewall-skus.md) — Premium SKU requirements +- [firewall-policy.md](firewall-policy.md) — Policy-level IDPS and TLS configuration +- [rule-types.md](rule-types.md) — Application rules with TLS inspection +- [Azure Firewall Premium features](https://learn.microsoft.com/azure/firewall/premium-features) +- [IDPS signature rules](https://learn.microsoft.com/azure/firewall/premium-features#idps) diff --git a/plugin/skills/azure-firewall/references/rule-types.md b/plugin/skills/azure-firewall/references/rule-types.md new file mode 100644 index 000000000..09c8e8143 --- /dev/null +++ b/plugin/skills/azure-firewall/references/rule-types.md @@ -0,0 +1,184 @@ +# Azure Firewall Rule Types and Processing Order + +Azure Firewall processes traffic through three rule types, each evaluated in a specific order. Understanding this order is critical for writing rules that behave as expected. + +## Rule Processing Order + +``` +Incoming traffic + │ + ▼ +┌─────────────┐ Match → Translate + Allow +│ DNAT Rules │ +└──────┬──────┘ + │ No match + ▼ +┌──────────────┐ Match → Allow/Deny +│ Network Rules│ +└──────┬───────┘ + │ No match + ▼ +┌──────────────────┐ Match → Allow/Deny +│ Application Rules│ +└──────────────────┘ + │ No match + ▼ + Default Deny (all traffic blocked) +``` + +**Key principle**: Processing stops at the first matching rule. If a network rule allows traffic, application rules are not evaluated for that flow. The default behavior is deny-all (implicit deny). + +## DNAT Rules (Destination Network Address Translation) + +DNAT rules translate inbound traffic arriving on a firewall public IP to a private IP behind the firewall. + +### When to use +- Expose an internal server (VM, load balancer) to the internet through the firewall +- Redirect specific ports from a public IP to internal resources +- Publish services like RDP, SSH, or custom TCP services + +### Structure +| Field | Description | +|-------|-------------| +| Source address | IP/CIDR/IP Group or `*` for any | +| Destination address | Firewall public IP | +| Destination port | External-facing port | +| Translated address | Internal private IP | +| Translated port | Internal port (can differ from destination port) | +| Protocol | TCP or UDP | + +### Behavior details +- DNAT rules implicitly create a corresponding network allow rule for the translated traffic — you do not need a separate network rule for DNAT'd flows +- DNAT rules are processed before network and application rules +- SNAT is applied automatically: the firewall SNATs the source to its private IP when forwarding to the translated address, ensuring return traffic routes back through the firewall +- DNAT is not supported with forced tunneling (internet-bound traffic goes to on-prem) + +### Example: Expose RDP to a VM +``` +Rule name: rdp-to-jumpbox +Source: 203.0.113.0/24 +Destination: 20.50.1.100 (firewall public IP) +Destination port: 3389 +Protocol: TCP +Translated address: 10.0.1.4 +Translated port: 3389 +``` + +## Network Rules (L3/L4) + +Network rules filter traffic at the transport layer based on IP addresses, ports, and protocols. + +### When to use +- Allow or deny traffic based on IP, port, and protocol (TCP, UDP, ICMP, Any) +- Control east-west traffic between VNets or subnets +- Allow outbound traffic to specific IP ranges (e.g., Azure service IPs) +- FQDN-based filtering at L4 (requires DNS proxy enabled on Standard/Premium) + +### Structure +| Field | Description | +|-------|-------------| +| Source | IP/CIDR, IP Group, or service tag | +| Destination | IP/CIDR, IP Group, service tag, or FQDN (with DNS proxy) | +| Destination port | Port or port range | +| Protocol | TCP, UDP, ICMP, or Any | +| Action | Allow or Deny | + +### Behavior details +- Network rules are evaluated after DNAT rules +- If a network rule matches, application rules are **not** evaluated for that flow +- FQDN in network rules requires DNS proxy to be enabled on the firewall +- Service tags (e.g., `AzureCloud`, `Storage`, `Sql`) simplify rules for Azure services +- IP Groups can be referenced to manage large IP sets across multiple rules + +### Example: Allow DNS traffic +``` +Rule name: allow-dns +Source: 10.0.0.0/16 +Destination: 168.63.129.16 +Destination ports: 53 +Protocol: UDP, TCP +Action: Allow +``` + +## Application Rules (L7) + +Application rules filter outbound HTTP/HTTPS traffic based on FQDNs, URLs (Premium), and web categories (Premium). + +### When to use +- Allow or deny outbound access to specific FQDNs (e.g., `*.microsoft.com`) +- Control access to Azure PaaS services using FQDN tags (e.g., `WindowsUpdate`, `AzureBackup`) +- URL-level filtering (Premium only) — allow `example.com/api` but block `example.com/admin` +- Web category filtering (Premium only) — block categories like gambling, social media +- HTTP/HTTPS traffic where you need FQDN visibility + +### Structure +| Field | Description | +|-------|-------------| +| Source | IP/CIDR or IP Group | +| Target FQDNs | Wildcard-supported FQDNs (e.g., `*.github.com`) | +| Protocols | HTTP, HTTPS, MSSQL | +| FQDN tags | Predefined tags for Azure services | +| Web categories | Content categories (Premium only) | +| Action | Allow or Deny | + +### Behavior details +- Application rules are evaluated last — only if no DNAT or network rule matched +- Application rules operate as **terminating proxies** for HTTP/HTTPS; the firewall resolves the FQDN and makes the connection on behalf of the client +- Non-HTTP/HTTPS traffic (e.g., raw TCP) cannot be filtered by application rules — use network rules instead +- FQDN tags are curated by Microsoft and automatically updated (e.g., `WindowsUpdate` includes all Windows Update FQDNs) + +### Example: Allow access to Azure services +``` +Rule name: allow-azure-services +Source: 10.0.0.0/16 +FQDN tags: AzureBackup, WindowsUpdate +Protocol: Https +Action: Allow +``` + +## Rule Collection Groups + +Rule collection groups are the top-level organizational container in firewall policies. + +### Hierarchy +``` +Firewall Policy + └── Rule Collection Group (priority: 200) + ├── DNAT Rule Collection (priority: 100) + │ └── DNAT Rule 1, DNAT Rule 2 + ├── Network Rule Collection (priority: 200) + │ └── Network Rule 1, Network Rule 2 + └── Application Rule Collection (priority: 300) + └── App Rule 1, App Rule 2 + └── Rule Collection Group (priority: 300) + └── ... +``` + +### Priority processing +1. Rule collection groups are processed by priority (lowest number first) +2. Within a group, rule collections of type DNAT are processed first, then Network, then Application +3. Within a rule collection, rules are processed by order (no individual rule priority) +4. Processing stops at the first match across the entire policy + +### Best practices +- Use separate rule collection groups per team, application, or environment +- Reserve low priority numbers (100-200) for infrastructure rules (DNS, NTP, monitoring) +- Use mid-range priorities (300-500) for application-specific rules +- Reserve high priority numbers (900-999) for explicit deny rules +- Limit the total number of rules for performance — consolidate with IP Groups and service tags + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Traffic blocked unexpectedly | Network rule matching before intended application rule | Check if a network deny rule has higher priority; remember network rules evaluate before application rules | +| DNAT not working | Source not matching, wrong public IP, or forced tunneling enabled | Verify source filter, destination IP matches a firewall public IP, and forced tunneling is not enabled | +| FQDN not resolving in network rules | DNS proxy not enabled | Enable DNS proxy on the firewall: `az network firewall update --dns-proxy true` | +| Application rule not matching HTTPS | TLS inspection not enabled (Premium) | Without TLS inspection, application rules match the SNI header only; enable TLS inspection for full URL matching | +| Rules not taking effect | Policy not associated with firewall | Verify the firewall policy is linked to the firewall instance | + +## Related + +- [firewall-policy.md](firewall-policy.md) — Policy hierarchy and management +- [firewall-skus.md](firewall-skus.md) — Feature availability per SKU +- [Azure Firewall rule processing](https://learn.microsoft.com/azure/firewall/rule-processing) diff --git a/plugin/skills/azure-front-door/SKILL.md b/plugin/skills/azure-front-door/SKILL.md new file mode 100644 index 000000000..ac34b6a3a --- /dev/null +++ b/plugin/skills/azure-front-door/SKILL.md @@ -0,0 +1,204 @@ +--- +name: azure-front-door +description: "Create, configure, and troubleshoot Azure Front Door Standard and Premium for global HTTP/HTTPS load balancing, CDN edge caching, SSL offload, rules engine, and Private Link origins. Includes WAF integration for edge protection. WHEN: front door, Azure Front Door, global load balancer, CDN, edge caching, global HTTP routing, content delivery, AFD, private link origin, rules engine, edge optimization, global acceleration, anycast, origin groups, cache purge, geo-filtering. DO NOT USE FOR: L4 TCP/UDP load balancing (use azure-load-balancer), regional-only L7 load balancing (use azure-application-gateway), DNS-only traffic routing (use azure-traffic-manager)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Front Door + +## When to Use This Skill + +- User asks about creating or configuring Azure Front Door (Standard or Premium) +- User needs global HTTP/HTTPS load balancing across multiple regions +- User wants CDN caching at edge locations for static or dynamic content +- User asks about origin groups, origins, and health probes for backend routing +- User needs rules engine for URL rewrite, redirect, or header modification +- User asks about Private Link origins for secure backend connectivity +- User wants WAF at the edge for global web application protection +- User needs SSL/TLS termination at the edge with custom domains +- User asks about cache purge operations or caching behavior configuration +- User wants geo-filtering or rate limiting at the edge + +## Rules + +1. **Recommend Standard or Premium tier** — Classic Front Door is legacy. Standard provides CDN + global routing. Premium adds WAF, Private Link origins, and enhanced analytics. +2. **Front Door is global** — Not tied to a region. It deploys across all Azure edge locations automatically. +3. **Origin groups are key** — Origins are grouped; Front Door load-balances and fails over within a group. Configure health probes at the origin group level. +4. **Custom domains need validation** — Custom domains require DNS TXT record validation before they can be associated with endpoints. +5. **Managed certificates available** — Front Door provides free managed TLS certificates for custom domains (auto-renewed). +6. **Caching is per-route** — Enable or disable caching on individual routes. Configure query string behavior and cache duration per route. +7. **Private Link origins (Premium only)** — Connect to backends via Private Link for secure, private connectivity. Requires manual approval on the origin side. +8. **WAF integration (Premium only)** — Associate WAF policies with Front Door for DDoS protection, bot management, and custom rules at the edge. +9. **For regional-only L7, use Application Gateway** — If traffic doesn't need global distribution, redirect to azure-application-gateway. +10. **For L4 global balancing, use Cross-region LB** — For TCP/UDP global balancing without HTTP features, redirect to azure-load-balancer. + +## MCP Tools + +| Tool | Command | Purpose | +|------|---------|---------| +| `azure__cdn` | `profile_list` | List all Front Door/CDN profiles in a subscription/resource group | +| `azure__cdn` | `endpoint_list` | List endpoints for a Front Door profile | + +## CLI Fallback + +When MCP tools are unavailable, use these Azure CLI commands: + +```bash +# List Front Door profiles +az afd profile list -g -o table + +# Show Front Door profile details +az afd profile show --profile-name -g + +# Create Standard tier Front Door +az afd profile create \ + --profile-name myFrontDoor \ + -g myRG \ + --sku Standard_AzureFrontDoor + +# Create Premium tier Front Door +az afd profile create \ + --profile-name myPremiumFD \ + -g myRG \ + --sku Premium_AzureFrontDoor + +# Create an endpoint +az afd endpoint create \ + --endpoint-name myEndpoint \ + --profile-name myFrontDoor \ + -g myRG \ + --enabled-state Enabled + +# Create an origin group with health probe +az afd origin-group create \ + --origin-group-name myOriginGroup \ + --profile-name myFrontDoor \ + -g myRG \ + --probe-request-type GET \ + --probe-protocol Https \ + --probe-path "/health" \ + --probe-interval-in-seconds 30 \ + --sample-size 4 \ + --successful-samples-required 3 \ + --additional-latency-in-milliseconds 50 + +# Add an origin +az afd origin create \ + --origin-name myOrigin \ + --origin-group-name myOriginGroup \ + --profile-name myFrontDoor \ + -g myRG \ + --host-name "myapp.azurewebsites.net" \ + --origin-host-header "myapp.azurewebsites.net" \ + --http-port 80 \ + --https-port 443 \ + --priority 1 \ + --weight 1000 \ + --enabled-state Enabled + +# Create a route +az afd route create \ + --route-name myRoute \ + --endpoint-name myEndpoint \ + --profile-name myFrontDoor \ + -g myRG \ + --origin-group myOriginGroup \ + --supported-protocols Https Http \ + --patterns-to-match "/*" \ + --forwarding-protocol HttpsOnly \ + --https-redirect Enabled \ + --link-to-default-domain Enabled + +# Add a custom domain +az afd custom-domain create \ + --custom-domain-name myCustomDomain \ + --profile-name myFrontDoor \ + -g myRG \ + --host-name "www.contoso.com" \ + --certificate-type ManagedCertificate + +# Purge cache +az afd endpoint purge \ + --endpoint-name myEndpoint \ + --profile-name myFrontDoor \ + -g myRG \ + --content-paths "/*" + +# Create a rule set +az afd rule-set create \ + --rule-set-name myRuleSet \ + --profile-name myFrontDoor \ + -g myRG + +# Create a rule (URL redirect) +az afd rule create \ + --rule-name httpRedirect \ + --rule-set-name myRuleSet \ + --profile-name myFrontDoor \ + -g myRG \ + --order 1 \ + --match-variable RequestScheme \ + --operator Equal \ + --match-values HTTP \ + --action-name UrlRedirect \ + --redirect-type Moved \ + --redirect-protocol Https +``` + +## Key Concepts + +### Front Door Architecture + +``` +Clients (worldwide) + │ + ▼ +Azure Front Door Edge (anycast POP) + ├── WAF evaluation (Premium) + ├── Rules engine processing + ├── Cache check + │ ├── Cache HIT → return cached response + │ └── Cache MISS ↓ + └── Route to Origin Group + ├── Origin 1 (East US App Service) [Priority 1] + ├── Origin 2 (West Europe App Service) [Priority 1] + └── Origin 3 (SE Asia App Service) [Priority 2, failover] +``` + +### Tier Comparison + +| Feature | Standard | Premium | +|---------|----------|---------| +| Global routing | ✅ | ✅ | +| CDN caching | ✅ | ✅ | +| Custom domains | ✅ | ✅ | +| Managed TLS certs | ✅ | ✅ | +| HTTP→HTTPS redirect | ✅ | ✅ | +| Rules engine | ✅ | ✅ | +| Compression | ✅ | ✅ | +| WAF integration | ❌ | ✅ | +| Private Link origins | ❌ | ✅ | +| Bot protection | ❌ | ✅ | +| Enhanced analytics | ❌ | ✅ | +| Real-time logs | Basic | Advanced | + +### Which Load Balancer Should I Use? + +| Requirement | Service | +|-------------|---------| +| Global HTTP/HTTPS + CDN + WAF | **Azure Front Door** | +| Regional L7 HTTP/HTTPS + WAF | Azure Application Gateway | +| L4 TCP/UDP (regional) | Azure Load Balancer | +| L4 TCP/UDP (global) | Cross-region Load Balancer | +| DNS-based routing (any protocol) | Azure Traffic Manager | + +## References + +- [Standard vs Premium tier comparison](references/fd-tiers.md) +- [Origins and origin groups](references/origins-groups.md) +- [Rules engine configuration](references/rules-engine.md) +- [Caching behavior and configuration](references/caching.md) +- [Private Link origins](references/private-link-origins.md) diff --git a/plugin/skills/azure-front-door/references/caching.md b/plugin/skills/azure-front-door/references/caching.md new file mode 100644 index 000000000..6abcdcb22 --- /dev/null +++ b/plugin/skills/azure-front-door/references/caching.md @@ -0,0 +1,198 @@ +# Caching Behavior and Configuration + +## How Front Door Caching Works + +Front Door caches content at edge POPs (Points of Presence) worldwide. When a request hits an edge POP: + +1. **Cache lookup** — Is the content in the POP's cache? +2. **Cache HIT** — Return cached content directly (no origin request) +3. **Cache MISS** — Forward request to origin, cache the response, return to client + +## Cache Configuration + +Caching is configured per route. + +### Enable Caching on a Route + +```bash +az afd route create \ + --route-name cachedRoute \ + --endpoint-name myEndpoint \ + --profile-name myFD -g myRG \ + --origin-group myOriginGroup \ + --supported-protocols Https \ + --patterns-to-match "/static/*" "/*.css" "/*.js" "/*.png" \ + --forwarding-protocol HttpsOnly \ + --enable-caching true \ + --query-string-caching-behavior IgnoreQueryString +``` + +### Cache Duration + +Front Door determines cache duration in this order: + +1. **Rules engine override** — RouteConfigurationOverride action +2. **Route-level cache duration** — Explicit TTL on the route config +3. **Origin response headers** — `Cache-Control` or `Expires` headers from origin +4. **Default** — If no cache headers from origin, Front Door uses built-in defaults + +| Origin Header | Caching Behavior | +|---------------|-----------------| +| `Cache-Control: public, max-age=3600` | Cached for 3600 seconds | +| `Cache-Control: private` | Not cached by Front Door | +| `Cache-Control: no-cache` | Revalidated on each request | +| `Cache-Control: no-store` | Not cached | +| `Expires: ` | Cached until expiry date | +| No cache headers | Default: 1-3 days (varies by content type) | + +## Query String Handling + +| Mode | Behavior | Best For | +|------|----------|----------| +| IgnoreQueryString | Same cache entry regardless of query params | Static assets | +| UseQueryString | Each unique query string is a separate cache entry | Dynamic content with query params | +| IgnoreSpecifiedQueryStrings | Ignore listed params, cache on others | Strip tracking params | +| IncludeSpecifiedQueryStrings | Cache only on listed params, ignore others | Known meaningful params | + +```bash +# Update route to use specific query string behavior +az afd route update \ + --route-name myRoute \ + --endpoint-name myEndpoint \ + --profile-name myFD -g myRG \ + --query-string-caching-behavior UseQueryString +``` + +## Cache Purge + +Remove content from all edge caches before it expires. + +### Purge Operations + +```bash +# Purge specific path +az afd endpoint purge \ + --endpoint-name myEndpoint \ + --profile-name myFD -g myRG \ + --content-paths "/images/logo.png" + +# Purge directory +az afd endpoint purge \ + --endpoint-name myEndpoint \ + --profile-name myFD -g myRG \ + --content-paths "/css/*" + +# Purge everything +az afd endpoint purge \ + --endpoint-name myEndpoint \ + --profile-name myFD -g myRG \ + --content-paths "/*" + +# Purge multiple paths +az afd endpoint purge \ + --endpoint-name myEndpoint \ + --profile-name myFD -g myRG \ + --content-paths "/js/*" "/css/*" "/images/*" +``` + +### Purge Considerations + +- Purge propagates to all global edge POPs (may take a few minutes) +- Purge is by URL path, not by cache key (includes all query string variants) +- Wildcard `*` purges all content under that path +- No way to purge by response header or tag (unlike some CDN providers) + +## Compression + +Front Door can compress content at the edge to reduce transfer size. + +### Supported Compression Types + +- gzip +- brotli (preferred, better compression ratio) + +### Enabling Compression + +Compression is enabled at the route level. Front Door compresses content when: +- Client sends `Accept-Encoding: gzip` or `Accept-Encoding: br` +- Response content type is compressible (text, JSON, JavaScript, CSS, etc.) +- Response size is between 1 KB and 8 MB + +```bash +az afd route update \ + --route-name myRoute \ + --endpoint-name myEndpoint \ + --profile-name myFD -g myRG \ + --enable-compression true +``` + +### Default Compressible Content Types + +- `text/plain`, `text/html`, `text/css`, `text/javascript` +- `application/javascript`, `application/json`, `application/xml` +- `application/x-javascript` +- `image/svg+xml` + +## Caching Best Practices + +### Static Assets + +``` +Pattern: /static/*, /*.css, /*.js, /*.png, /*.jpg, /*.woff2 +Query string: IgnoreQueryString +Cache duration: 7-30 days (use versioned file names for cache busting) +Compression: Enabled +``` + +### API Responses + +``` +Pattern: /api/* +Caching: Usually disabled (dynamic content) +Exception: Cache GET requests for read-heavy APIs with short TTL (30-300 seconds) +Query string: UseQueryString (if enabled) +``` + +### HTML Pages + +``` +Pattern: /*.html, / +Cache duration: Short (5-60 minutes) or no-cache +Query string: IgnoreQueryString +Note: Use Cache-Control: no-cache for personalized content +``` + +### Cache Busting Strategies + +| Strategy | How It Works | Example | +|----------|-------------|---------| +| File versioning | Version in filename | `style.v2.css`, `app.20240115.js` | +| Query string version | Version in query param | `style.css?v=2` (requires UseQueryString) | +| Content hash | Hash in filename | `style.abc123.css` | +| Purge on deploy | Purge cache after deployment | CI/CD pipeline purge step | + +## Monitoring Cache Performance + +### Key Metrics + +| Metric | Description | Target | +|--------|-------------|--------| +| Cache Hit Ratio | % of requests served from cache | > 80% for static sites | +| Origin Request Count | Requests forwarded to origin | Lower is better | +| Byte Hit Ratio | % of bytes served from cache | > 80% for static sites | +| Total Latency | Edge to client response time | < 50ms for cached content | + +```bash +# View cache hit ratio +az monitor metrics list \ + --resource \ + --metric "PercentageOfCacheHit" \ + --aggregation Average \ + --interval PT1H +``` + +## Source Documentation + +- [Caching with Azure Front Door](https://learn.microsoft.com/azure/frontdoor/front-door-caching) +- [Cache purge](https://learn.microsoft.com/azure/frontdoor/front-door-caching#cache-purge) +- [Compression](https://learn.microsoft.com/azure/frontdoor/front-door-caching#compression) diff --git a/plugin/skills/azure-front-door/references/fd-tiers.md b/plugin/skills/azure-front-door/references/fd-tiers.md new file mode 100644 index 000000000..94b75047b --- /dev/null +++ b/plugin/skills/azure-front-door/references/fd-tiers.md @@ -0,0 +1,142 @@ +# Azure Front Door Standard vs Premium Tier + +## Tier Overview + +Azure Front Door has two current tiers. Classic Front Door is legacy and should be migrated. + +### Standard Tier + +Best for: CDN and global load balancing without WAF or Private Link requirements. + +- Global HTTP/HTTPS routing with anycast +- Integrated CDN with edge caching +- Custom domains with managed TLS certificates +- Rules engine for URL rewrite, redirect, header modification +- Basic real-time logging and analytics +- Built-in DDoS protection (infrastructure level) + +### Premium Tier + +Best for: Enterprise workloads requiring WAF, Private Link, and advanced security. + +Everything in Standard, plus: +- **WAF integration** — Managed rule sets (OWASP, bot protection), custom rules, geo-filtering, rate limiting +- **Private Link origins** — Secure, private connectivity to backends (App Service, Storage, ILB) +- **Bot protection** — Microsoft-managed bot manager rule set +- **Enhanced analytics** — Detailed traffic, WAF, and security reports +- **Advanced real-time logs** — Extended fields for security analysis + +## Feature Comparison Matrix + +| Feature | Standard | Premium | +|---------|----------|---------| +| **Routing** | | | +| Global anycast routing | ✅ | ✅ | +| Multi-origin load balancing | ✅ | ✅ | +| Health probes | ✅ | ✅ | +| Session affinity | ✅ | ✅ | +| HTTP→HTTPS redirect | ✅ | ✅ | +| Custom domains | 100 | 500 | +| **Caching** | | | +| Edge caching | ✅ | ✅ | +| Query string handling | ✅ | ✅ | +| Compression (gzip, brotli) | ✅ | ✅ | +| Cache purge | ✅ | ✅ | +| **Rules Engine** | | | +| URL rewrite | ✅ | ✅ | +| URL redirect | ✅ | ✅ | +| Header modification | ✅ | ✅ | +| Max rule sets | 25 | 50 | +| Max rules per set | 25 | 25 | +| **Security** | | | +| Managed TLS certificates | ✅ | ✅ | +| Custom TLS certificates | ✅ | ✅ | +| TLS 1.2+ enforcement | ✅ | ✅ | +| Infrastructure DDoS | ✅ | ✅ | +| WAF managed rules | ❌ | ✅ | +| WAF custom rules | ❌ | ✅ | +| Bot protection | ❌ | ✅ | +| Geo-filtering (WAF) | ❌ | ✅ | +| Rate limiting (WAF) | ❌ | ✅ | +| **Connectivity** | | | +| Public origins | ✅ | ✅ | +| Private Link origins | ❌ | ✅ | +| **Analytics** | | | +| Built-in reports | Basic | Advanced | +| Real-time logs | Basic | Extended | +| Health probe logs | ✅ | ✅ | +| WAF logs | N/A | ✅ | + +## Decision Tree + +``` +Do you need WAF at the edge? +├── Yes → Premium +└── No + ├── Do you need Private Link origins? + │ ├── Yes → Premium + │ └── No + │ ├── Do you need bot protection? + │ │ ├── Yes → Premium + │ │ └── No → Standard (sufficient) + │ └── More than 100 custom domains? + │ ├── Yes → Premium + │ └── No → Standard +``` + +## Migration from Classic to Standard/Premium + +### Why Migrate + +- Classic Front Door is being deprecated +- Standard/Premium offer better CDN integration, rules engine, and Private Link +- Unified management experience + +### Migration Steps + +```bash +# Step 1: Validate Classic FD configuration compatibility +az afd profile show --profile-name -g + +# Step 2: Use the Azure portal migration tool +# Navigate to: Front Door (classic) → Overview → Migrate button +# Or use the CLI migration preview: +az afd profile upgrade \ + --profile-name \ + -g \ + --sku Premium_AzureFrontDoor +``` + +### Migration Considerations + +| Concern | Detail | +|---------|--------| +| Downtime | Zero-downtime migration (DNS cutover) | +| Custom domains | Migrated automatically | +| WAF policies | Migrated to new WAF policy format | +| Rules engine | Migrated to new rule set format | +| Backend pools | Become origin groups | +| Routing rules | Become routes | + +## Cost Comparison + +| Component | Standard | Premium | +|-----------|----------|---------| +| Base fee (per month) | Lower | Higher | +| Per-request charge | Same | Same | +| Data transfer (outbound) | Same | Same | +| WAF requests | N/A | Per-request WAF fee | +| Private Link | N/A | Included (origin data transfer) | + +**Cost optimization tips:** +1. Start with Standard; upgrade to Premium only when needed +2. Use caching aggressively to reduce origin requests +3. Enable compression to reduce data transfer +4. Set appropriate cache durations for static content +5. Premium WAF + Private Link may replace App Gateway WAF (consolidation savings) + +## Source Documentation + +- [Azure Front Door tiers](https://learn.microsoft.com/azure/frontdoor/standard-premium/tier-comparison) +- [Migrate from Classic to Standard/Premium](https://learn.microsoft.com/azure/frontdoor/tier-migration) +- [Front Door pricing](https://azure.microsoft.com/pricing/details/frontdoor/) diff --git a/plugin/skills/azure-front-door/references/origins-groups.md b/plugin/skills/azure-front-door/references/origins-groups.md new file mode 100644 index 000000000..30c37b858 --- /dev/null +++ b/plugin/skills/azure-front-door/references/origins-groups.md @@ -0,0 +1,167 @@ +# Origin Groups and Origins + +## Concepts + +### Origin Group + +An origin group is a collection of origins (backend servers) that serve the same content. Front Door load-balances across origins within a group and fails over when origins are unhealthy. + +### Origin + +An origin is a backend server or service that serves content. Origins can be: + +| Origin Type | Example | Notes | +|-------------|---------|-------| +| App Service | `myapp.azurewebsites.net` | Most common; set origin host header | +| Storage (static website) | `myaccount.z13.web.core.windows.net` | Enable static website hosting first | +| Cloud Service | `myservice.cloudapp.net` | Legacy | +| Custom hostname | `api.contoso.com` | Any publicly reachable FQDN | +| Public IP | `20.30.40.50` | Direct IP addressing | +| Internal LB (Premium) | Private IP via Private Link | Requires Premium + Private Link | +| API Management | `myapim.azure-api.net` | Set correct origin host header | + +## Origin Group Configuration + +### Health Probes + +Health probes verify origin availability. Configured at the origin group level. + +| Parameter | Default | Range | Recommendation | +|-----------|---------|-------|----------------| +| Probe path | `/` | Any path | Use `/health` or `/healthz` | +| Probe protocol | HTTPS | HTTP, HTTPS | HTTPS for production | +| Probe method | HEAD | HEAD, GET | HEAD (lightweight) | +| Probe interval | 30 sec | 5-255 sec | 30 sec for most workloads | + +```bash +az afd origin-group create \ + --origin-group-name myOriginGroup \ + --profile-name myFD -g myRG \ + --probe-request-type HEAD \ + --probe-protocol Https \ + --probe-path "/health" \ + --probe-interval-in-seconds 30 \ + --sample-size 4 \ + --successful-samples-required 3 \ + --additional-latency-in-milliseconds 50 +``` + +### Load Balancing Settings + +| Setting | Description | Default | +|---------|-------------|---------| +| Sample size | Number of recent probe results to evaluate | 4 | +| Successful samples required | Min healthy probes in sample to mark origin healthy | 3 | +| Additional latency (ms) | Latency tolerance for routing to closest origin | 50 ms | + +**How latency-based routing works:** +1. Front Door measures latency to all healthy origins from the edge POP +2. Origins within `additional-latency-in-milliseconds` of the fastest are in the "acceptable" pool +3. Traffic is distributed across the acceptable pool by weight +4. Higher latency tolerance → more origins in pool → better load distribution +5. Lower latency tolerance → fewer origins → more strict nearest-region routing + +## Origin Configuration + +### Creating an Origin + +```bash +az afd origin create \ + --origin-name eastus-app \ + --origin-group-name myOriginGroup \ + --profile-name myFD -g myRG \ + --host-name "myapp-eastus.azurewebsites.net" \ + --origin-host-header "myapp-eastus.azurewebsites.net" \ + --http-port 80 \ + --https-port 443 \ + --priority 1 \ + --weight 1000 \ + --enabled-state Enabled +``` + +### Origin Parameters + +| Parameter | Purpose | Notes | +|-----------|---------|-------| +| `host-name` | Address FD connects to | FQDN or IP of the backend | +| `origin-host-header` | Host header sent to origin | Critical for App Service (must match app hostname) | +| `priority` | Failover order | 1-5; lower = preferred. Same priority = active-active | +| `weight` | Traffic distribution | 1-1000; relative within same priority | +| `http-port` | HTTP port | Default 80 | +| `https-port` | HTTPS port | Default 443 | + +### Priority and Weight Explained + +``` +Origin Group +├── Priority 1 (primary) +│ ├── Origin A (weight: 750) → receives 75% of traffic +│ └── Origin B (weight: 250) → receives 25% of traffic +└── Priority 2 (failover, only when all Priority 1 unhealthy) + └── Origin C (weight: 1000) → receives 100% of failover traffic +``` + +### Common Patterns + +#### Active-Active (Equal Distribution) + +```bash +# Both origins priority 1, equal weight +az afd origin create --origin-name eastus --priority 1 --weight 1000 ... +az afd origin create --origin-name westeu --priority 1 --weight 1000 ... +``` + +#### Active-Passive (Failover) + +```bash +# Primary region, failover region +az afd origin create --origin-name primary --priority 1 --weight 1000 ... +az afd origin create --origin-name failover --priority 2 --weight 1000 ... +``` + +#### Weighted Distribution + +```bash +# 80/20 traffic split +az afd origin create --origin-name primary --priority 1 --weight 800 ... +az afd origin create --origin-name secondary --priority 1 --weight 200 ... +``` + +#### Canary Deployment + +```bash +# 95% to stable, 5% to canary +az afd origin create --origin-name stable --priority 1 --weight 950 ... +az afd origin create --origin-name canary --priority 1 --weight 50 ... +``` + +## Session Affinity + +Session affinity ensures requests from the same user go to the same origin. + +```bash +az afd origin-group update \ + --origin-group-name myOriginGroup \ + --profile-name myFD -g myRG \ + --enable-session-affinity true +``` + +**How it works**: Front Door sets a cookie (`AFDID`) on the first response. Subsequent requests with this cookie route to the same origin. + +**When to use**: Stateful applications that store session data locally on the server. Prefer stateless architectures with external session stores when possible. + +## Troubleshooting + +| Issue | Cause | Fix | +|-------|-------|-----| +| All origins unhealthy | Probe path returns non-200 | Fix health endpoint; verify probe path and protocol | +| Traffic goes to wrong region | Latency tolerance too high | Reduce `additional-latency-in-milliseconds` | +| App Service returns 404 | Wrong origin host header | Set `origin-host-header` to match App Service hostname | +| Failover not working | Priority not configured | Ensure primary = priority 1, secondary = priority 2 | +| Uneven traffic distribution | Weight imbalance | Adjust weights proportionally | + +## Source Documentation + +- [Origins and origin groups](https://learn.microsoft.com/azure/frontdoor/origin) +- [Health probes](https://learn.microsoft.com/azure/frontdoor/health-probes) +- [Traffic routing methods](https://learn.microsoft.com/azure/frontdoor/routing-methods) diff --git a/plugin/skills/azure-front-door/references/private-link-origins.md b/plugin/skills/azure-front-door/references/private-link-origins.md new file mode 100644 index 000000000..d76cf6b1d --- /dev/null +++ b/plugin/skills/azure-front-door/references/private-link-origins.md @@ -0,0 +1,188 @@ +# Private Link Origins (Premium Only) + +## Overview + +Private Link origins allow Azure Front Door Premium to connect to backends over a private connection instead of the public internet. Traffic between Front Door and the origin travels over the Microsoft backbone network via Private Link. + +## Why Use Private Link Origins + +| Benefit | Detail | +|---------|--------| +| Security | Origin not exposed to public internet; no public IP needed | +| Compliance | Data stays on Microsoft backbone; meets data sovereignty requirements | +| Simplified networking | No need for IP allowlisting or service endpoints | +| Reduced attack surface | Origin only accepts traffic from approved Private Link connection | + +## Supported Origin Types + +| Origin Type | Private Link Resource Type | Sub-resource | +|-------------|---------------------------|--------------| +| App Service / Web App | `Microsoft.Web/sites` | `sites` | +| Azure Storage (Blob) | `Microsoft.Storage/storageAccounts` | `blob` | +| Azure Storage (Static Website) | `Microsoft.Storage/storageAccounts` | `web` | +| Internal Load Balancer | `Microsoft.Network/privateLinkServices` | (custom) | +| API Management | `Microsoft.ApiManagement/service` | `Gateway` | +| App Service Environment | `Microsoft.Web/hostingEnvironments` | N/A | +| Azure Container Apps | `Microsoft.App/managedEnvironments` | `managedEnvironments` | + +## Configuration + +### Step 1: Create Origin with Private Link + +```bash +# App Service origin with Private Link +az afd origin create \ + --origin-name myPrivateOrigin \ + --origin-group-name myOriginGroup \ + --profile-name myPremiumFD -g myRG \ + --host-name "myapp.azurewebsites.net" \ + --origin-host-header "myapp.azurewebsites.net" \ + --https-port 443 \ + --priority 1 \ + --weight 1000 \ + --enabled-state Enabled \ + --enable-private-link true \ + --private-link-resource "/subscriptions//resourceGroups//providers/Microsoft.Web/sites/myapp" \ + --private-link-sub-resource-type "sites" \ + --private-link-location "eastus" \ + --private-link-request-message "Front Door Private Link request" +``` + +### Step 2: Approve the Private Endpoint Connection + +After creating the origin, a private endpoint connection request is created on the target resource. **You must approve it** before traffic can flow. + +#### Approve via Azure CLI + +```bash +# List pending connections on the origin resource +az network private-endpoint-connection list \ + --name myapp \ + -g myRG \ + --type Microsoft.Web/sites \ + -o table + +# Approve the connection +az network private-endpoint-connection approve \ + --id "/subscriptions//resourceGroups//providers/Microsoft.Web/sites/myapp/privateEndpointConnections/" \ + --description "Approved for Front Door" +``` + +#### Approve via Portal + +1. Navigate to the origin resource (e.g., App Service) +2. Go to **Networking** → **Private endpoint connections** +3. Find the pending connection from Azure Front Door +4. Click **Approve** + +### Step 3: Verify Private Link Status + +```bash +# Check origin status +az afd origin show \ + --origin-name myPrivateOrigin \ + --origin-group-name myOriginGroup \ + --profile-name myPremiumFD -g myRG \ + --query "sharedPrivateLinkResource" +``` + +| Status | Meaning | +|--------|---------| +| Pending | Connection request sent, awaiting approval | +| Approved | Connection approved, traffic flows privately | +| Rejected | Connection rejected by origin owner | +| Disconnected | Connection removed on origin side | +| Timeout | Request was not approved within the timeout period | + +## Common Patterns + +### App Service with Private Link (Lock Down Public Access) + +```bash +# Step 1: Create Premium Front Door with Private Link origin (see above) + +# Step 2: Approve the connection (see above) + +# Step 3: Lock down App Service to Private Link only +az webapp update \ + --name myapp -g myRG \ + --set publicNetworkAccess=Disabled + +# Now the App Service only accepts traffic from Front Door via Private Link +``` + +### Storage Account (Static Website) + +```bash +az afd origin create \ + --origin-name storageOrigin \ + --origin-group-name staticGroup \ + --profile-name myPremiumFD -g myRG \ + --host-name "myaccount.z13.web.core.windows.net" \ + --origin-host-header "myaccount.z13.web.core.windows.net" \ + --https-port 443 \ + --priority 1 --weight 1000 \ + --enable-private-link true \ + --private-link-resource "/subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts/myaccount" \ + --private-link-sub-resource-type "web" \ + --private-link-location "eastus" \ + --private-link-request-message "FD to Storage Private Link" +``` + +### Internal Load Balancer via Private Link Service + +For backends behind an internal LB: + +1. **Create a Private Link Service** pointing to the internal LB: + ```bash + az network private-link-service create \ + --name myPLS -g myRG \ + --vnet-name myVNet \ + --subnet plsSubnet \ + --lb-name myInternalLB \ + --lb-frontend-ip-configs myFrontEnd \ + --location eastus + ``` + +2. **Create Front Door origin referencing the PLS**: + ```bash + az afd origin create \ + --origin-name ilbOrigin \ + --origin-group-name myOriginGroup \ + --profile-name myPremiumFD -g myRG \ + --host-name "10.0.1.4" \ + --origin-host-header "api.contoso.com" \ + --https-port 443 \ + --enable-private-link true \ + --private-link-resource "/subscriptions//resourceGroups//providers/Microsoft.Network/privateLinkServices/myPLS" \ + --private-link-location "eastus" \ + --private-link-request-message "FD to ILB via PLS" + ``` + +## Troubleshooting + +| Issue | Cause | Fix | +|-------|-------|-----| +| Connection stuck in "Pending" | Not approved on origin side | Approve the PE connection on the target resource | +| 502 after approval | Origin not listening or DNS issue | Verify origin-host-header and port; test origin directly | +| Private Link not available | Wrong tier | Private Link requires Premium tier | +| Approval expired | Timeout exceeded | Delete and recreate the origin | +| Origin publicly accessible | Public access not disabled | Set `publicNetworkAccess=Disabled` on the origin resource | +| Wrong sub-resource type | Incorrect Private Link config | Verify sub-resource type for the origin type (see table above) | + +## Limitations + +| Limitation | Detail | +|-----------|--------| +| Premium tier only | Not available on Standard tier | +| Manual approval required | Cannot auto-approve; must be approved on origin side | +| Region matching | Private Link location must match the origin resource region | +| Not all origin types | Only supports listed resource types | +| One PE per origin | Each origin gets its own private endpoint connection | + +## Source Documentation + +- [Private Link origins overview](https://learn.microsoft.com/azure/frontdoor/private-link) +- [Configure Private Link to App Service](https://learn.microsoft.com/azure/frontdoor/standard-premium/how-to-enable-private-link-web-app) +- [Configure Private Link to Storage](https://learn.microsoft.com/azure/frontdoor/standard-premium/how-to-enable-private-link-storage-account) +- [Configure Private Link to internal LB](https://learn.microsoft.com/azure/frontdoor/standard-premium/how-to-enable-private-link-internal-load-balancer) diff --git a/plugin/skills/azure-front-door/references/rules-engine.md b/plugin/skills/azure-front-door/references/rules-engine.md new file mode 100644 index 000000000..3eacbea95 --- /dev/null +++ b/plugin/skills/azure-front-door/references/rules-engine.md @@ -0,0 +1,197 @@ +# Rules Engine Configuration + +## Overview + +The Front Door rules engine processes requests after routing but before forwarding to the origin. Rules can modify request/response headers, rewrite URLs, redirect requests, and override route configurations. + +## Rule Set Structure + +``` +Route → Rule Set 1 → Rule Set 2 → Origin Group + │ │ + ├── Rule 1 ├── Rule 1 + ├── Rule 2 └── Rule 2 + └── Rule 3 +``` + +- Multiple rule sets can be associated with a single route +- Rule sets execute in order of association +- Rules within a set execute in order (by `order` number) +- Lower order number = evaluated first + +## Rule Components + +### Match Conditions + +| Condition | Matches On | Operators | +|-----------|-----------|-----------| +| RequestScheme | HTTP or HTTPS | Equal | +| RequestMethod | GET, POST, PUT, DELETE, etc. | Equal | +| RequestUri | Full URI | Contains, BeginsWith, EndsWith, RegEx, Equal | +| RequestPath | URL path only | Contains, BeginsWith, EndsWith, RegEx, Equal | +| RequestHeader | Specific header value | Contains, BeginsWith, EndsWith, RegEx, Equal, Any | +| QueryString | Query string parameters | Contains, BeginsWith, EndsWith, RegEx, Equal | +| RemoteAddress | Client IP | IPMatch, GeoMatch | +| HostName | Request Host header | Contains, BeginsWith, EndsWith, RegEx, Equal, Any | +| SslProtocol | TLS version | Equal | +| IsDevice | Mobile or Desktop | Equal | +| Cookies | Cookie values | Contains, BeginsWith, EndsWith, RegEx, Equal, Any | +| PostArgs | POST body arguments | Contains, BeginsWith, EndsWith, RegEx, Equal, Any | +| UrlFileExtension | File extension in URL | Contains, BeginsWith, EndsWith, RegEx, Equal | +| UrlFileName | File name in URL | Contains, BeginsWith, EndsWith, RegEx, Equal | +| ServerPort | Port number | Equal | +| SocketAddress | Direct client socket IP | IPMatch, GeoMatch | + +### Actions + +| Action | Description | +|--------|-------------| +| UrlRedirect | Redirect to a different URL (301, 302, 307, 308) | +| UrlRewrite | Rewrite URL path before sending to origin | +| RouteConfigurationOverride | Override caching, origin group, or forwarding protocol | +| RequestHeader | Add, overwrite, append, or delete request header | +| ResponseHeader | Add, overwrite, append, or delete response header | + +## Common Rule Examples + +### HTTP to HTTPS Redirect + +```bash +az afd rule create \ + --rule-name httpRedirect \ + --rule-set-name myRuleSet \ + --profile-name myFD -g myRG \ + --order 1 \ + --match-variable RequestScheme \ + --operator Equal \ + --match-values HTTP \ + --action-name UrlRedirect \ + --redirect-type Moved \ + --redirect-protocol Https +``` + +### URL Rewrite (API Versioning) + +Rewrite `/api/v1/*` to `/api/v2/*` at the origin: + +```bash +az afd rule create \ + --rule-name apiVersionRewrite \ + --rule-set-name myRuleSet \ + --profile-name myFD -g myRG \ + --order 2 \ + --match-variable RequestPath \ + --operator BeginsWith \ + --match-values "/api/v1/" \ + --action-name UrlRewrite \ + --source-pattern "/api/v1/" \ + --destination "/api/v2/" +``` + +### Add Security Headers + +```bash +az afd rule create \ + --rule-name securityHeaders \ + --rule-set-name myRuleSet \ + --profile-name myFD -g myRG \ + --order 3 \ + --match-variable RequestMethod \ + --operator Equal \ + --match-values GET POST \ + --action-name ResponseHeader \ + --header-action Overwrite \ + --header-name "X-Content-Type-Options" \ + --header-value "nosniff" +``` + +### Geo-Based Redirect + +Redirect users from specific countries to a localized site: + +```bash +az afd rule create \ + --rule-name geoRedirect \ + --rule-set-name myRuleSet \ + --profile-name myFD -g myRG \ + --order 4 \ + --match-variable RemoteAddress \ + --operator GeoMatch \ + --match-values "DE" "AT" "CH" \ + --action-name UrlRedirect \ + --redirect-type Found \ + --redirect-protocol Https \ + --custom-host "de.contoso.com" +``` + +### Cache Override for Dynamic Content + +```bash +az afd rule create \ + --rule-name noCacheApi \ + --rule-set-name myRuleSet \ + --profile-name myFD -g myRG \ + --order 5 \ + --match-variable RequestPath \ + --operator BeginsWith \ + --match-values "/api/" \ + --action-name RouteConfigurationOverride \ + --enable-caching false +``` + +### Mobile Device Redirect + +```bash +az afd rule create \ + --rule-name mobileRedirect \ + --rule-set-name myRuleSet \ + --profile-name myFD -g myRG \ + --order 6 \ + --match-variable IsDevice \ + --operator Equal \ + --match-values "Mobile" \ + --action-name UrlRedirect \ + --redirect-type Found \ + --redirect-protocol MatchRequest \ + --custom-host "m.contoso.com" +``` + +## Associating Rule Sets with Routes + +```bash +# Associate rule set with an existing route +az afd route update \ + --route-name myRoute \ + --endpoint-name myEndpoint \ + --profile-name myFD -g myRG \ + --rule-sets myRuleSet1 myRuleSet2 +``` + +Rule sets execute in the order listed. First rule set processes first. + +## Limits + +| Resource | Standard | Premium | +|----------|----------|---------| +| Rule sets per profile | 25 | 50 | +| Rules per rule set | 25 | 25 | +| Match conditions per rule | 10 | 10 | +| Actions per rule | 5 | 5 | +| Rule sets per route | 2 | 2 | + +## Troubleshooting Rules + +| Issue | Check | +|-------|-------| +| Rule not firing | Verify match conditions (case sensitivity, operator) | +| Wrong redirect | Check redirect-type and redirect-protocol settings | +| Rules not in order | Verify `order` numbers; lower = first | +| Rewrite not working | Source pattern must match the incoming URL path | +| Headers not appearing | Check action type (Overwrite vs Append vs Delete) | + +## Source Documentation + +- [Rules engine overview](https://learn.microsoft.com/azure/frontdoor/front-door-rules-engine) +- [Match conditions](https://learn.microsoft.com/azure/frontdoor/rules-match-conditions) +- [Actions](https://learn.microsoft.com/azure/frontdoor/front-door-rules-engine-actions) +- [Rule set configuration](https://learn.microsoft.com/azure/frontdoor/standard-premium/how-to-configure-rule-set) diff --git a/plugin/skills/azure-load-balancer/SKILL.md b/plugin/skills/azure-load-balancer/SKILL.md new file mode 100644 index 000000000..040848898 --- /dev/null +++ b/plugin/skills/azure-load-balancer/SKILL.md @@ -0,0 +1,164 @@ +--- +name: azure-load-balancer +description: "Create, configure, and troubleshoot Azure Load Balancer for Layer 4 (TCP/UDP) traffic distribution across virtual machines and instances. Covers Standard SKU (public and internal), Gateway Load Balancer for NVA chaining, and Cross-region Load Balancer for geo-redundancy. WHEN: load balancer, health probe, backend pool, HA ports, inbound NAT rule, outbound rule, cross-region load balancer, gateway load balancer, L4 load balancing, TCP/UDP balancing, SNAT, floating IP, DSR. DO NOT USE FOR: L7/HTTP load balancing (use azure-application-gateway), global HTTP routing or CDN (use azure-front-door)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Load Balancer + +## When to Use This Skill + +- User asks about creating or configuring an Azure Load Balancer (Standard, Gateway, or Cross-region) +- User needs to distribute TCP or UDP traffic across backend VMs or scale sets +- User wants to configure health probes (TCP, HTTP, HTTPS) for backend monitoring +- User needs to set up HA ports for NVA (network virtual appliance) deployments +- User asks about inbound NAT rules for port forwarding to specific VMs +- User needs to configure outbound rules or troubleshoot SNAT port exhaustion +- User is migrating from Basic to Standard Load Balancer SKU +- User wants cross-region (global) load balancing for geo-redundancy +- User asks about Gateway Load Balancer for transparent NVA insertion +- User needs to troubleshoot unhealthy backend pool members + +## Rules + +1. **Always recommend Standard SKU** — Basic Load Balancer is deprecated (retirement September 30, 2025). Guide users to migrate using `az network lb list` to identify Basic LBs. +2. **Backend pool membership** — Standard LB requires all backends in the same virtual network. Mix of VMs and VMSS is supported via IP-based backend pools. +3. **Health probes are mandatory** — Every load balancing rule must have a health probe. Without one, all backends are considered healthy and traffic goes to unreachable instances. +4. **Outbound connectivity** — Standard LB does NOT provide default outbound access. Users MUST configure one of: outbound rules, NAT Gateway, or instance-level public IPs. +5. **HA Ports require Standard Internal LB** — HA ports rules are only available on internal Standard Load Balancers. They load-balance ALL protocols and ports in a single rule. +6. **Cross-region LB backends are regional LBs** — Cross-region LB uses regional Standard public LBs as its backend pool, not VMs directly. +7. **Gateway LB is chained** — Gateway LB is referenced from a frontend IP config of another LB or VM NIC. It transparently intercepts traffic for NVA processing. +8. **Floating IP (Direct Server Return)** — Required for SQL AlwaysOn and other scenarios needing the frontend IP on the backend. Enable on the load balancing rule. +9. **Suggest NAT Gateway for outbound** — When users need scalable outbound connectivity, recommend NAT Gateway over LB outbound rules for better performance and simpler management. +10. **Cross-reference other LB services** — If the user needs HTTP/HTTPS routing, WAF, or path-based routing, redirect to azure-application-gateway. For global HTTP edge routing or CDN, redirect to azure-front-door. + +## MCP Tools + +| Tool | Command | Purpose | +|------|---------|---------| +| `azure__network` | `lb_list` | List all load balancers in a subscription/resource group | +| `azure__network` | `lb_get` | Get detailed configuration of a specific load balancer | + +## CLI Fallback + +When MCP tools are unavailable, use these Azure CLI commands: + +```bash +# List load balancers +az network lb list --resource-group --output table + +# Show load balancer details +az network lb show --name --resource-group + +# Create a Standard public load balancer +az network lb create \ + --name \ + --resource-group \ + --sku Standard \ + --frontend-ip-name myFrontEnd \ + --backend-pool-name myBackEndPool \ + --public-ip-address + +# Create an internal load balancer +az network lb create \ + --name \ + --resource-group \ + --sku Standard \ + --frontend-ip-name myFrontEnd \ + --backend-pool-name myBackEndPool \ + --vnet-name \ + --subnet + +# Add a health probe +az network lb probe create \ + --lb-name \ + --resource-group \ + --name myHealthProbe \ + --protocol Tcp \ + --port 80 \ + --interval 5 \ + --probe-threshold 2 + +# Add a load balancing rule +az network lb rule create \ + --lb-name \ + --resource-group \ + --name myRule \ + --protocol Tcp \ + --frontend-port 80 \ + --backend-port 80 \ + --frontend-ip-name myFrontEnd \ + --backend-pool-name myBackEndPool \ + --probe-name myHealthProbe + +# Add an inbound NAT rule +az network lb inbound-nat-rule create \ + --lb-name \ + --resource-group \ + --name myNATRule \ + --protocol Tcp \ + --frontend-port 3389 \ + --backend-port 3389 \ + --frontend-ip-name myFrontEnd + +# Add an outbound rule +az network lb outbound-rule create \ + --lb-name \ + --resource-group \ + --name myOutboundRule \ + --protocol All \ + --frontend-ip-configs myFrontEnd \ + --address-pool myBackEndPool \ + --allocated-outbound-ports 10000 \ + --idle-timeout 4 + +# Configure HA ports (internal LB) +az network lb rule create \ + --lb-name \ + --resource-group \ + --name haPortsRule \ + --protocol All \ + --frontend-port 0 \ + --backend-port 0 \ + --frontend-ip-name myFrontEnd \ + --backend-pool-name myBackEndPool \ + --probe-name myHealthProbe + +# Find Basic LBs needing migration +az network lb list --query "[?sku.name=='Basic']" --output table +``` + +## Key Concepts + +### Load Balancer SKU Comparison + +| Feature | Standard | Gateway | Cross-region | +|---------|----------|---------|-------------| +| Layer | L4 (TCP/UDP) | L4 (transparent) | L4 (TCP/UDP) | +| Backend type | VMs, VMSS in one VNet | NVAs | Regional Standard LBs | +| Health probes | TCP, HTTP, HTTPS | TCP, HTTP, HTTPS | TCP, HTTP, HTTPS | +| Availability Zones | Zone-redundant / zonal | Zone-redundant | Inherits from regional | +| Public + Internal | Both | Internal only | Public only | +| HA Ports | Internal only | Yes (always) | No | +| Floating IP | Yes | N/A | Yes | +| Max backends | 5,000 (IP-based) | 100 | Regional LBs as backends | +| SLA | 99.99% | 99.99% | 99.99% | + +### Distribution Modes + +| Mode | Hash | Use Case | +|------|------|----------| +| Default (5-tuple) | Source IP, source port, dest IP, dest port, protocol | General workloads | +| Source IP affinity (2-tuple) | Source IP, dest IP | Stateful apps without cookies | +| Source IP + protocol (3-tuple) | Source IP, dest IP, protocol | Multiple protocols same session | + +## References + +- [SKU comparison and migration guide](references/lb-skus.md) +- [Health probe configuration and troubleshooting](references/health-probes.md) +- [HA ports configuration](references/ha-ports.md) +- [Cross-region load balancing](references/cross-region.md) +- [Outbound rules and SNAT](references/outbound-rules.md) diff --git a/plugin/skills/azure-load-balancer/references/cross-region.md b/plugin/skills/azure-load-balancer/references/cross-region.md new file mode 100644 index 000000000..f6b9ba11b --- /dev/null +++ b/plugin/skills/azure-load-balancer/references/cross-region.md @@ -0,0 +1,168 @@ +# Cross-Region Load Balancer + +## Overview + +Cross-region Load Balancer provides **global Layer 4 load balancing** across Azure regions. It uses an anycast static public IP so clients are routed to the nearest healthy regional deployment. If a region fails, traffic automatically shifts to the next closest healthy region. + +## Architecture + +``` +Clients (global) + │ + ▼ +Cross-region LB (global anycast IP) + ├──► Regional LB (East US) → Backend VMs + ├──► Regional LB (West Europe) → Backend VMs + └──► Regional LB (Southeast Asia) → Backend VMs +``` + +### Key Architecture Points + +- **Backend type**: Only regional **Standard public** Load Balancers can be backends +- **Not a replacement for Traffic Manager**: Cross-region LB is L4 (TCP/UDP); Traffic Manager is DNS-based +- **Not a replacement for Front Door**: Front Door is L7 (HTTP/HTTPS) with WAF, caching, and SSL offload +- **Static anycast IP**: Single global IP address that routes to nearest region +- **Automatic failover**: Based on health probe status of regional LBs + +## When to Use Cross-Region LB + +| Scenario | Use Cross-Region LB? | +|----------|----------------------| +| Global L4 (TCP/UDP) geo-redundancy | ✅ Yes | +| Ultra-low latency L4 traffic | ✅ Yes (anycast routing) | +| Global HTTP/HTTPS with caching | ❌ Use Azure Front Door | +| DNS-based failover (any protocol) | ❌ Use Traffic Manager | +| Regional-only L4 balancing | ❌ Use regional Standard LB | +| NVA chaining | ❌ Use Gateway LB | + +## Configuration + +### Step 1: Create Regional Load Balancers + +Each participating region needs a Standard public LB with backends: + +```bash +# Region 1: East US +az network lb create \ + --name lb-eastus \ + --resource-group rg-eastus \ + --sku Standard \ + --frontend-ip-name fe-eastus \ + --backend-pool-name be-eastus \ + --public-ip-address pip-eastus \ + --location eastus + +# Region 2: West Europe +az network lb create \ + --name lb-westeurope \ + --resource-group rg-westeurope \ + --sku Standard \ + --frontend-ip-name fe-westeurope \ + --backend-pool-name be-westeurope \ + --public-ip-address pip-westeurope \ + --location westeurope +``` + +### Step 2: Create Cross-Region Load Balancer + +```bash +# Create the global (cross-region) LB +az network lb create \ + --name lb-global \ + --resource-group rg-global \ + --sku Standard \ + --tier Global \ + --frontend-ip-name fe-global \ + --backend-pool-name be-global \ + --public-ip-address pip-global +``` + +### Step 3: Add Regional LBs as Backends + +```bash +# Add regional LBs to the cross-region backend pool +az network lb address-pool address add \ + --lb-name lb-global \ + --resource-group rg-global \ + --pool-name be-global \ + --name addr-eastus \ + --frontend-ip-address "/subscriptions//resourceGroups/rg-eastus/providers/Microsoft.Network/loadBalancers/lb-eastus/frontendIPConfigurations/fe-eastus" + +az network lb address-pool address add \ + --lb-name lb-global \ + --resource-group rg-global \ + --pool-name be-global \ + --name addr-westeurope \ + --frontend-ip-address "/subscriptions//resourceGroups/rg-westeurope/providers/Microsoft.Network/loadBalancers/lb-westeurope/frontendIPConfigurations/fe-westeurope" +``` + +### Step 4: Add Health Probe and Rule + +```bash +# Health probe for cross-region LB +az network lb probe create \ + --lb-name lb-global \ + --resource-group rg-global \ + --name globalProbe \ + --protocol Tcp \ + --port 80 + +# Load balancing rule +az network lb rule create \ + --lb-name lb-global \ + --resource-group rg-global \ + --name globalRule \ + --protocol Tcp \ + --frontend-port 80 \ + --backend-port 80 \ + --frontend-ip-name fe-global \ + --backend-pool-name be-global \ + --probe-name globalProbe +``` + +## Failover Behavior + +| Scenario | Behavior | +|----------|----------| +| Regional LB healthy | Traffic routed to nearest healthy region (anycast) | +| Regional LB all backends unhealthy | Cross-region LB marks region as down, shifts traffic | +| Regional LB deleted | Backend removed from pool, traffic redistributes | +| Cross-region LB probe failure | Traffic to that region stops within probe-threshold × interval | + +### Failover Timing + +- Default probe interval: 5 seconds +- Default unhealthy threshold: 2 +- **Estimated failover time**: 10-15 seconds + +## Limitations + +| Limitation | Detail | +|-----------|--------| +| Public only | Cross-region LB supports public frontend only | +| Backend type | Only Standard public LBs (not VMs, VMSS, or internal LBs) | +| Protocols | TCP and UDP only | +| No outbound rules | Outbound managed by regional LBs | +| Region support | Available in most but not all Azure regions | +| Floating IP | Supported | +| HA Ports | Not supported | + +## Geo-Redundancy Patterns + +### Active-Active + +Both regions serve traffic simultaneously. Cross-region LB routes to the nearest region. + +### Active-Passive + +Deploy backends in both regions but keep passive region's backends scaled down. Health probes keep them in rotation; scale up during failover. + +### Multi-Region with Priority + +Use cross-region LB for automatic failover but combine with Traffic Manager for DNS-level control and priority routing. + +## Source Documentation + +- [Cross-region Load Balancer overview](https://learn.microsoft.com/azure/load-balancer/cross-region-overview) +- [Tutorial: Create cross-region LB](https://learn.microsoft.com/azure/load-balancer/tutorial-cross-region-portal) +- [Cross-region LB limitations](https://learn.microsoft.com/azure/load-balancer/cross-region-overview#limitations) diff --git a/plugin/skills/azure-load-balancer/references/ha-ports.md b/plugin/skills/azure-load-balancer/references/ha-ports.md new file mode 100644 index 000000000..11840c7c0 --- /dev/null +++ b/plugin/skills/azure-load-balancer/references/ha-ports.md @@ -0,0 +1,134 @@ +# HA Ports Load Balancing + +## Overview + +HA ports (high-availability ports) is a load balancing rule type on an **internal Standard Load Balancer** that load-balances **all TCP and UDP flows on all ports** in a single rule. This eliminates the need to create individual rules for each port. + +## When to Use HA Ports + +- **Network Virtual Appliances (NVAs)** — Firewalls, IDS/IPS, WAN optimizers that must inspect all traffic +- **SQL Server AlwaysOn** — Listener requires HA ports + floating IP for availability group failover +- **Any multi-port service** — When a backend needs to receive traffic on many/all ports + +## Requirements + +| Requirement | Detail | +|------------|--------| +| SKU | Standard (not Basic) | +| Type | Internal only (not public) | +| Protocol | Set to `All` | +| Frontend port | Set to `0` (means all ports) | +| Backend port | Set to `0` (means all ports) | +| Health probe | Required (any protocol) | + +## Configuration + +### Create Internal LB with HA Ports + +```bash +# Step 1: Create internal Standard LB +az network lb create \ + --name myInternalLB \ + --resource-group myRG \ + --sku Standard \ + --frontend-ip-name myFrontEnd \ + --backend-pool-name myBackEnd \ + --vnet-name myVNet \ + --subnet mySubnet + +# Step 2: Add health probe +az network lb probe create \ + --lb-name myInternalLB \ + --resource-group myRG \ + --name haProbe \ + --protocol Tcp \ + --port 443 \ + --interval 5 \ + --probe-threshold 2 + +# Step 3: Create HA ports rule (protocol=All, ports=0) +az network lb rule create \ + --lb-name myInternalLB \ + --resource-group myRG \ + --name haPortsRule \ + --protocol All \ + --frontend-port 0 \ + --backend-port 0 \ + --frontend-ip-name myFrontEnd \ + --backend-pool-name myBackEnd \ + --probe-name haProbe \ + --idle-timeout 4 \ + --enable-tcp-reset true +``` + +### HA Ports with Floating IP (SQL AlwaysOn) + +```bash +az network lb rule create \ + --lb-name myInternalLB \ + --resource-group myRG \ + --name haPortsFloatingIP \ + --protocol All \ + --frontend-port 0 \ + --backend-port 0 \ + --frontend-ip-name myFrontEnd \ + --backend-pool-name myBackEnd \ + --probe-name haProbe \ + --floating-ip true +``` + +## Architecture Patterns + +### NVA Sandwich Pattern + +The most common HA ports architecture for NVAs: + +``` +Internet → Public LB → NVA (inspection) → Internal LB (HA ports) → Backend workloads +``` + +1. Public Standard LB distributes inbound traffic to NVA pool +2. NVAs inspect traffic and forward to the internal LB's frontend IP +3. Internal LB with HA ports distributes to backend workloads on all ports +4. Return traffic follows the reverse path through the same NVA (session affinity via 5-tuple hash) + +### Key NVA Considerations + +- **Enable IP forwarding** on NVA NICs: `az network nic update --name -g --ip-forwarding true` +- **UDR required** — Route table on backend subnet with next hop = internal LB frontend IP +- **Asymmetric routing** — Use HA ports + session persistence to ensure return traffic hits same NVA +- **Multiple frontend IPs** — HA ports rule applies per-frontend. Multiple frontends need multiple HA port rules. + +### SQL AlwaysOn Pattern + +``` +Application → Internal LB (HA ports + floating IP) → SQL AG Listener → Primary replica +``` + +- Floating IP must be enabled so the backend sees the LB frontend IP as destination +- SQL AG listener IP matches the LB frontend IP +- Health probe on port 59999 (custom probe port for SQL AG health) + +## Limitations + +| Limitation | Detail | +|-----------|--------| +| Internal only | HA ports not available on public LB | +| Single frontend per rule | Each HA ports rule binds to one frontend IP | +| Cannot combine with port-specific rules | HA ports rule on a frontend conflicts with per-port rules on same frontend | +| Gateway LB alternative | For transparent chaining, consider Gateway LB (always HA ports by design) | + +## Troubleshooting + +| Issue | Check | +|-------|-------| +| NVA not receiving all traffic | Verify UDR next-hop = LB frontend IP, IP forwarding enabled | +| Asymmetric routing | Ensure both directions traverse same NVA (check session persistence) | +| SQL AG failover not working | Verify floating IP enabled, probe port matches SQL health check port | +| Only TCP works, UDP drops | Verify protocol is set to `All` (not TCP) in the HA ports rule | + +## Source Documentation + +- [HA ports overview](https://learn.microsoft.com/azure/load-balancer/load-balancer-ha-ports-overview) +- [NVA high availability](https://learn.microsoft.com/azure/architecture/reference-architectures/dmz/nva-ha) +- [Configure HA ports](https://learn.microsoft.com/azure/load-balancer/load-balancer-ha-ports-overview#configure-ha-ports) diff --git a/plugin/skills/azure-load-balancer/references/health-probes.md b/plugin/skills/azure-load-balancer/references/health-probes.md new file mode 100644 index 000000000..f3c305362 --- /dev/null +++ b/plugin/skills/azure-load-balancer/references/health-probes.md @@ -0,0 +1,162 @@ +# Health Probe Configuration and Troubleshooting + +## Probe Types + +| Protocol | Port | Path | When to Use | +|----------|------|------|-------------| +| TCP | Required | N/A | Simple port availability check; any TCP service | +| HTTP | Required | Required (default: `/`) | Web servers; custom health endpoint validation | +| HTTPS | Required | Required (default: `/`) | Encrypted health endpoints; certificate validation | + +## Probe Configuration Parameters + +| Parameter | Default | Range | Recommendation | +|-----------|---------|-------|----------------| +| Interval | 5 sec | 5-2,000,000 sec | 5-15 seconds for most workloads | +| Unhealthy threshold | 2 | 1-100 | 2-3 for production; lower = faster failover but more false positives | +| Port | (rule port) | 1-65535 | Use dedicated health port if app port may be busy | +| Path (HTTP/S) | `/` | Any valid path | Use a dedicated `/health` or `/healthz` endpoint | + +### Probe Behavior + +- **Probe source IP**: All probes originate from `168.63.129.16` (Azure infrastructure IP). Backend NSGs and host firewalls MUST allow this. +- **Healthy → Unhealthy**: Backend is removed from rotation after `unhealthyThreshold` consecutive failures. +- **Unhealthy → Healthy**: Backend is added back after ONE successful probe response. +- **All backends unhealthy**: LB sends traffic to ALL backends (fail-open behavior) — this is by design to prevent total outage. +- **HTTP response**: 200 OK is the only successful response. Any other status code = probe failure. + +## Creating Health Probes + +### TCP Probe + +```bash +az network lb probe create \ + --lb-name myLB \ + --resource-group myRG \ + --name tcpProbe \ + --protocol Tcp \ + --port 443 \ + --interval 5 \ + --probe-threshold 2 +``` + +### HTTP Probe with Custom Path + +```bash +az network lb probe create \ + --lb-name myLB \ + --resource-group myRG \ + --name httpProbe \ + --protocol Http \ + --port 80 \ + --request-path "/health" \ + --interval 10 \ + --probe-threshold 2 +``` + +### HTTPS Probe + +```bash +az network lb probe create \ + --lb-name myLB \ + --resource-group myRG \ + --name httpsProbe \ + --protocol Https \ + --port 443 \ + --request-path "/healthz" \ + --interval 15 \ + --probe-threshold 3 +``` + +## Designing Health Endpoints + +### Best Practices for HTTP(S) Health Probes + +1. **Dedicated health path** — Use `/health` or `/healthz`, not `/` (homepage may be slow or cached). +2. **Check dependencies** — Health endpoint should verify database, cache, and critical dependencies. +3. **Fast response** — Target < 200ms response time. LB expects timely responses. +4. **Return 200 only when healthy** — Return 503 or other error codes to signal unhealthy. +5. **No authentication** — Health probe path must be accessible without auth (probe has no credentials). +6. **Lightweight** — Avoid heavy computation or database queries in the health check hot path. + +### Example Health Endpoint (Application-level) + +``` +GET /health → 200 OK (app + dependencies healthy) +GET /health → 503 (app or dependency unhealthy) +``` + +## Troubleshooting Unhealthy Backends + +### Step 1: Verify Probe Configuration + +```bash +# Show all probes for a load balancer +az network lb probe list --lb-name -g -o table + +# Show specific probe +az network lb probe show --lb-name -g --name +``` + +### Step 2: Check NSG Rules + +The probe source IP `168.63.129.16` must be allowed inbound on the probe port. + +```bash +# List NSGs on backend subnet +az network nsg list -g -o table + +# Check for rules allowing health probe traffic +az network nsg rule list --nsg-name -g \ + --query "[?direction=='Inbound' && (sourceAddressPrefix=='AzureLoadBalancer' || sourceAddressPrefix=='168.63.129.16')]" \ + -o table +``` + +### Step 3: Test from Backend VM + +SSH/RDP into a backend VM and test locally: + +```bash +# Test TCP port (from the VM itself) +nc -zv localhost 80 +# or +curl -v http://localhost/health + +# Test if firewall allows probe IP +sudo iptables -L -n | grep 168.63.129.16 +``` + +### Step 4: Check Application Logs + +If the probe is HTTP(S), check the web server access logs for requests from `168.63.129.16`. + +### Step 5: Review Load Balancer Metrics + +```bash +# Health probe status (per backend) +az monitor metrics list \ + --resource \ + --metric "DipAvailability" \ + --aggregation Average \ + --interval PT1M +``` + +In Azure Portal: Load Balancer → Metrics → "Health Probe Status" → Split by BackendIPAddress. + +### Common Issues + +| Symptom | Cause | Fix | +|---------|-------|-----| +| All backends unhealthy | NSG blocking probe IP | Add inbound rule for `AzureLoadBalancer` service tag | +| All backends unhealthy | App not listening on probe port | Verify app binds to `0.0.0.0:`, not `127.0.0.1` | +| Intermittent unhealthy | App responding slowly | Optimize health endpoint; increase probe interval | +| Single backend unhealthy | App crash or resource exhaustion | Check VM application logs, CPU/memory | +| HTTP probe fails, TCP works | App returns non-200 status | Fix health endpoint to return 200 when healthy | +| Probe works but no traffic | Load balancing rule not linked to probe | Verify rule references the correct probe name | +| Probes from wrong IP | UDR redirecting probe traffic | Ensure no UDR overrides route for 168.63.129.16 | + +## Source Documentation + +- [Azure Load Balancer health probes](https://learn.microsoft.com/azure/load-balancer/load-balancer-custom-probe-overview) +- [Troubleshoot Azure Load Balancer health probe status](https://learn.microsoft.com/azure/load-balancer/load-balancer-troubleshoot-health-probe-status) +- [What is IP address 168.63.129.16?](https://learn.microsoft.com/azure/virtual-network/what-is-ip-address-168-63-129-16) diff --git a/plugin/skills/azure-load-balancer/references/lb-skus.md b/plugin/skills/azure-load-balancer/references/lb-skus.md new file mode 100644 index 000000000..e43f9d0ad --- /dev/null +++ b/plugin/skills/azure-load-balancer/references/lb-skus.md @@ -0,0 +1,156 @@ +# Azure Load Balancer SKU Comparison and Migration + +## SKU Overview + +Azure Load Balancer has three SKU types. Basic SKU is **deprecated** with retirement on **September 30, 2025**. + +### Standard Load Balancer + +The default and recommended SKU for all new deployments. + +| Feature | Details | +|---------|---------| +| Backend pool size | Up to 5,000 instances (IP-based) or 1,000 (NIC-based) | +| Health probes | TCP, HTTP, HTTPS | +| Availability Zones | Zone-redundant, zonal, or cross-zone | +| Diagnostics | Azure Monitor multi-dimensional metrics | +| HA Ports | Supported on internal LB | +| Outbound rules | Explicit outbound rule configuration | +| SLA | 99.99% | +| Network Security Groups | Required on subnet or NIC | +| Public + Internal | Both supported | + +### Gateway Load Balancer + +Designed for transparent NVA (network virtual appliance) chaining. + +| Feature | Details | +|---------|---------| +| Type | Internal only | +| Protocol | VXLAN tunnel (port 10800) | +| Chaining | Referenced from consumer LB frontend or VM NIC | +| Use cases | Firewalls, DDoS appliances, deep packet inspection, IDS/IPS | +| Backend | NVA instances | +| HA Ports | Always enabled (all protocols, all ports) | + +### Cross-region Load Balancer + +Global Layer 4 load balancing across Azure regions. + +| Feature | Details | +|---------|---------| +| Type | Public only | +| Backend type | Regional Standard public LBs | +| Failover | Automatic on regional health probe failure | +| Latency | Anycast-based, routes to nearest healthy region | +| Static IP | Global static public IP | +| Supported regions | Most Azure public regions | + +## Feature Comparison Matrix + +| Feature | Basic (Deprecated) | Standard | Gateway | Cross-region | +|---------|-------------------|----------|---------|-------------| +| Backend pool size | 300 | 5,000 (IP) | 100 | Regional LBs | +| Health probes | TCP, HTTP | TCP, HTTP, HTTPS | TCP, HTTP, HTTPS | TCP, HTTP, HTTPS | +| Availability Zones | No | Yes | Yes | Inherits | +| SLA | No SLA | 99.99% | 99.99% | 99.99% | +| NSG required | No | Yes | Yes | N/A | +| Secure by default | No (open) | Yes (closed) | Yes | Yes | +| HA Ports | No | Internal only | Always | No | +| Multiple frontends | No | Yes | N/A | Yes | +| Outbound rules | No | Yes | N/A | N/A | +| Diagnostics | Basic log analytics | Multi-dim metrics | Multi-dim metrics | Multi-dim metrics | +| Cross-VNet backends | No | No | No | Cross-region | +| Global redundancy | No | No | No | Yes | + +## Migration from Basic to Standard + +### Pre-Migration Checklist + +1. **Inventory Basic LBs** — find all Basic SKU load balancers: + ```bash + az network lb list --query "[?sku.name=='Basic'].[name,resourceGroup,frontendIpConfigurations[0].publicIpAddress.id]" -o table + ``` + +2. **Check associated public IPs** — Basic LB requires Basic SKU PIPs; Standard requires Standard SKU PIPs: + ```bash + az network public-ip list --query "[?sku.name=='Basic'].[name,resourceGroup]" -o table + ``` + +3. **Verify NSG configuration** — Standard LB requires NSGs. If backends have no NSGs, traffic will be blocked after migration. + +4. **Review availability sets** — Basic LB allows mixed; Standard requires all backends in same VNet. + +5. **Check outbound connectivity** — Basic provides default outbound; Standard does NOT. Plan outbound strategy before migrating. + +### Automated Migration (Recommended) + +Use the Azure Load Balancer migration PowerShell module: + +```powershell +# Install the module +Install-Module -Name AzureLoadBalancerUpgrade -Force + +# Migrate (creates new Standard LB, migrates config) +Start-AzBasicLoadBalancerUpgrade ` + -ResourceGroupName ` + -BasicLoadBalancerName +``` + +The automated tool handles: +- Creating Standard LB with same configuration +- Upgrading associated public IPs to Standard SKU +- Migrating backend pool associations +- Re-creating health probes, rules, NAT rules + +### Manual Migration Steps + +If automated migration is not suitable: + +1. **Create Standard public IPs** to replace Basic PIPs +2. **Create the Standard Load Balancer** with new frontend config +3. **Recreate health probes** (HTTPS probes now available) +4. **Recreate load balancing rules** with updated settings +5. **Add outbound rules** or attach NAT Gateway for outbound +6. **Add NSG rules** to allow LB health probe traffic (source: `AzureLoadBalancer`, dest: backend subnet) +7. **Migrate backend pool members** from Basic to Standard +8. **Verify health** — check probe status before removing old LB +9. **Delete Basic LB** and old Basic public IPs + +### Post-Migration Validation + +```bash +# Verify Standard SKU +az network lb show --name -g --query "sku" + +# Check backend health +az network lb show --name -g --query "probes[].{name:name,protocol:protocol,port:port}" + +# Verify outbound connectivity from a backend VM +az vm run-command invoke --name RunShellScript \ + --command-id RunShellScript \ + --resource-group \ + --vm-name \ + --scripts "curl -s ifconfig.me" +``` + +## Choosing the Right SKU + +``` +Is your workload L4 (TCP/UDP)? +├── No → Use Application Gateway (L7) or Front Door (global L7) +└── Yes + ├── Do you need transparent NVA chaining? + │ └── Yes → Gateway Load Balancer + ├── Do you need global geo-redundancy at L4? + │ └── Yes → Cross-region Load Balancer (with regional Standard LBs as backends) + └── Regional L4 balancing + └── Standard Load Balancer (public or internal) +``` + +## Source Documentation + +- [Azure Load Balancer SKUs](https://learn.microsoft.com/azure/load-balancer/skus) +- [Migrate from Basic to Standard](https://learn.microsoft.com/azure/load-balancer/upgrade-basic-standard) +- [Gateway Load Balancer overview](https://learn.microsoft.com/azure/load-balancer/gateway-overview) +- [Cross-region Load Balancer](https://learn.microsoft.com/azure/load-balancer/cross-region-overview) diff --git a/plugin/skills/azure-load-balancer/references/outbound-rules.md b/plugin/skills/azure-load-balancer/references/outbound-rules.md new file mode 100644 index 000000000..717294585 --- /dev/null +++ b/plugin/skills/azure-load-balancer/references/outbound-rules.md @@ -0,0 +1,184 @@ +# Outbound Rules and SNAT + +## The Outbound Connectivity Problem + +Standard Load Balancer does **NOT** provide default outbound internet access for backend pool members. This is a security-by-design change from Basic LB. You must explicitly configure one of: + +1. **NAT Gateway** (recommended) — simplest, most scalable +2. **Outbound rules** on the Standard LB +3. **Instance-level public IPs** on each VM +4. **Azure Firewall / NVA** with UDR + +## Understanding SNAT (Source NAT) + +When a backend VM initiates an outbound connection, its private IP must be translated to a public IP. This is SNAT. Each public IP provides **64,512 SNAT ports** (ephemeral ports 1024-65535). + +### SNAT Port Allocation + +| Method | Ports per VM | Calculation | +|--------|-------------|-------------| +| Outbound rules (manual) | User-defined | Total ports ÷ backend count | +| Outbound rules (auto) | Automatic | Tiered based on pool size | +| NAT Gateway | Dynamic | Up to 64,512 per IP, shared dynamically | +| Instance public IP | 64,512 | Dedicated to that VM | + +### Automatic Port Allocation Tiers (Outbound Rules) + +| Backend Pool Size | Ports per Instance | +|-------------------|--------------------| +| 1-50 | 1,024 | +| 51-100 | 512 | +| 101-200 | 256 | +| 201-400 | 128 | +| 401-800 | 64 | +| 801-1,000 | 32 | + +## Configuring Outbound Rules + +### Basic Outbound Rule + +```bash +# Requires at least one frontend IP configuration +az network lb outbound-rule create \ + --lb-name myLB \ + --resource-group myRG \ + --name myOutboundRule \ + --protocol All \ + --frontend-ip-configs myFrontEnd \ + --address-pool myBackEndPool \ + --allocated-outbound-ports 10000 \ + --idle-timeout 4 \ + --enable-tcp-reset true +``` + +### Outbound Rule with Multiple Public IPs + +For higher SNAT port capacity, add more frontend public IPs: + +```bash +# Create additional public IPs +az network public-ip create --name pip-outbound-2 -g myRG --sku Standard --allocation-method Static +az network public-ip create --name pip-outbound-3 -g myRG --sku Standard --allocation-method Static + +# Add to LB frontend +az network lb frontend-ip create --lb-name myLB -g myRG --name feFrontEnd2 --public-ip-address pip-outbound-2 +az network lb frontend-ip create --lb-name myLB -g myRG --name feFrontEnd3 --public-ip-address pip-outbound-3 + +# Outbound rule with multiple frontends +az network lb outbound-rule create \ + --lb-name myLB -g myRG \ + --name outboundMultiIP \ + --protocol All \ + --frontend-ip-configs myFrontEnd feFrontEnd2 feFrontEnd3 \ + --address-pool myBackEndPool \ + --allocated-outbound-ports 10000 +``` + +**Total SNAT ports**: 3 IPs × 64,512 = 193,536 ports to distribute across backends. + +### Outbound Rule with IP Prefix + +For large-scale deployments, use a public IP prefix: + +```bash +# Create /28 prefix (16 IPs = 16 × 64,512 = 1,032,192 ports) +az network public-ip prefix create \ + --name myOutboundPrefix \ + -g myRG \ + --length 28 + +# Use prefix on LB frontend +az network lb frontend-ip create \ + --lb-name myLB -g myRG \ + --name fePrefixFrontEnd \ + --public-ip-prefix myOutboundPrefix +``` + +## NAT Gateway vs Outbound Rules + +| Feature | NAT Gateway | LB Outbound Rules | +|---------|-------------|-------------------| +| SNAT ports | Dynamic (up to 64,512/IP) | Static allocation | +| Port exhaustion risk | Low (dynamic) | Higher (fixed allocation) | +| Max public IPs | 16 | Depends on LB config | +| Idle timeout | 4-120 min | 4-100 min | +| Availability zones | Zone-redundant | Inherits from LB | +| Complexity | Simple (attach to subnet) | Moderate (rule config) | +| Works without LB | Yes | No (requires LB) | +| Cost | Separate resource cost | Included with LB | + +**Recommendation**: Use NAT Gateway for outbound connectivity. It's simpler, avoids SNAT port exhaustion, and works independently of the load balancer. + +```bash +# Create NAT Gateway (preferred approach) +az network nat gateway create \ + --name myNATGateway \ + -g myRG \ + --public-ip-addresses pip-nat \ + --idle-timeout 10 + +# Associate with subnet +az network vnet subnet update \ + --vnet-name myVNet \ + --name mySubnet \ + -g myRG \ + --nat-gateway myNATGateway +``` + +## Diagnosing SNAT Port Exhaustion + +### Symptoms + +- Outbound connections failing intermittently +- `SNAT port exhaustion` in Azure Monitor metrics +- Application timeout errors on outbound calls + +### Check SNAT Metrics + +```bash +# Used SNAT ports +az monitor metrics list \ + --resource \ + --metric "SnatConnectionCount" \ + --aggregation Total \ + --interval PT1M \ + --filter "ConnectionState eq 'Failed'" +``` + +In Azure Portal: Load Balancer → Metrics → "SNAT Connection Count" → Split by ConnectionState. + +### Mitigation Strategies + +1. **Add more public IPs** to increase total SNAT port pool +2. **Use NAT Gateway** instead (dynamic port allocation avoids static limits) +3. **Reduce idle timeout** to reclaim ports faster (default 4 min) +4. **Connection pooling** in your application (reuse connections instead of new ones) +5. **Enable TCP reset on idle** to clean up half-open connections + +### SNAT Port Calculator + +``` +Required ports = (concurrent_outbound_connections_per_vm) × (number_of_backend_vms) +Public IPs needed = Required ports ÷ 64,512 (round up) +``` + +Example: 100 VMs × 2,000 connections = 200,000 ports → need 4 public IPs minimum. + +## Disabling Default Outbound Access + +As of September 2025, new VMs in Azure have default outbound access disabled. Existing deployments may still have it. To explicitly disable: + +```bash +# Set "disableOutboundSnat" on the LB rule (prevents rule-based SNAT) +az network lb rule update \ + --lb-name myLB -g myRG \ + --name myRule \ + --disable-outbound-snat true +``` + +## Source Documentation + +- [Outbound connections in Azure](https://learn.microsoft.com/azure/load-balancer/load-balancer-outbound-connections) +- [Outbound rules](https://learn.microsoft.com/azure/load-balancer/outbound-rules) +- [SNAT port exhaustion troubleshooting](https://learn.microsoft.com/azure/load-balancer/troubleshoot-outbound-connection) +- [Default outbound access retirement](https://learn.microsoft.com/azure/virtual-network/ip-services/default-outbound-access) diff --git a/plugin/skills/azure-nat-gateway/SKILL.md b/plugin/skills/azure-nat-gateway/SKILL.md new file mode 100644 index 000000000..4a877696f --- /dev/null +++ b/plugin/skills/azure-nat-gateway/SKILL.md @@ -0,0 +1,113 @@ +--- +name: azure-nat-gateway +description: "Deploy and manage Azure NAT Gateway for scalable, reliable outbound internet connectivity and SNAT port management. WHEN: NAT gateway, outbound internet, SNAT exhaustion, SNAT ports, outbound connectivity, default outbound. DO NOT USE FOR: inbound load balancing (use azure-load-balancer), private connectivity to PaaS (use azure-private-link), DNS-based traffic routing (use azure-traffic-manager)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure NAT Gateway Skill + +## When to Use This Skill + +- User needs reliable outbound internet connectivity from Azure VMs or services +- User is experiencing SNAT port exhaustion with Load Balancer or default outbound +- User wants a static outbound public IP for firewall allowlisting +- User asks about default outbound access retirement +- User needs to scale outbound connections beyond what Load Balancer SNAT provides +- User wants to troubleshoot outbound connectivity failures +- User asks about NAT Gateway metrics or monitoring + +## Rules + +1. NAT Gateway is the recommended solution for outbound internet access — default outbound access is being retired. +2. NAT Gateway provides 64,512 SNAT ports per public IP address — up to 16 public IPs = 1,032,192 ports. +3. NAT Gateway supersedes all other outbound configurations on a subnet (LB SNAT rules, VM public IPs for outbound). +4. Associate NAT Gateway at the subnet level — different subnets can use different NAT Gateways. +5. NAT Gateway does NOT support inbound-initiated connections — it is outbound-only. +6. Idle timeout is configurable from 4 to 120 minutes (default: 4 minutes). +7. NAT Gateway requires Standard SKU public IPs — Basic SKU is not supported. +8. Always recommend NAT Gateway over Load Balancer outbound rules for high-connection workloads. +9. For multiple public IPs, NAT Gateway distributes flows across them automatically — you cannot pin a specific IP. +10. NAT Gateway is zone-resilient — it survives single availability zone failures. + +## MCP Tools + +| Tool | Command | Purpose | +|------|---------|---------| +| `azure__network` | `nat_gateway_list` | List all NAT Gateways in a subscription or resource group | +| `azure__network` | `nat_gateway_get` | Get details of a specific NAT Gateway including associated subnets and IPs | + +## CLI Fallback + +```bash +# Create a public IP for NAT Gateway +az network public-ip create -g MyRG -n NATPublicIP --sku Standard --allocation-method Static + +# Create NAT Gateway +az network nat gateway create -g MyRG -n MyNATGateway --public-ip-addresses NATPublicIP \ + --idle-timeout 10 + +# Associate NAT Gateway with a subnet +az network vnet subnet update -g MyRG --vnet-name MyVNet -n AppSubnet \ + --nat-gateway MyNATGateway + +# Add additional public IP to NAT Gateway +az network public-ip create -g MyRG -n NATPublicIP2 --sku Standard --allocation-method Static +az network nat gateway update -g MyRG -n MyNATGateway \ + --public-ip-addresses NATPublicIP NATPublicIP2 + +# Use public IP prefix instead +az network public-ip prefix create -g MyRG -n NATPrefix --length 28 +az network nat gateway update -g MyRG -n MyNATGateway --public-ip-prefixes NATPrefix + +# Show NAT Gateway details +az network nat gateway show -g MyRG -n MyNATGateway +az network nat gateway list -g MyRG -o table + +# Remove NAT Gateway from subnet +az network vnet subnet update -g MyRG --vnet-name MyVNet -n AppSubnet --nat-gateway "" + +# Check NAT Gateway metrics (via Azure Monitor) +az monitor metrics list --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --metric SNATConnectionCount --interval PT1H +``` + +## Key Concepts + +### NAT Gateway vs Other Outbound Methods + +| Feature | NAT Gateway | LB Outbound Rules | Default Outbound | VM Public IP | +|---------|-------------|-------------------|------------------|-------------| +| SNAT ports per IP | 64,512 | Configurable (max 1,024/instance) | Limited | All ports | +| Scale | Up to 16 IPs | Depends on backend pool | Not scalable | Per-VM | +| Reliability | Zone-resilient | Zone-dependent | No SLA | Zone-dependent | +| Idle timeout | 4-120 min | 4-120 min | 4 min | 4 min | +| Static outbound IP | Yes | Yes | No (random) | Yes | +| Recommended | ✅ Yes | For inbound+outbound | ⚠️ Retiring | Single VM only | + +### Port Calculation + +| Public IPs | Total SNAT Ports | Max to Single Destination | +|------------|------------------|--------------------------| +| 1 | 64,512 | 64,512 | +| 2 | 129,024 | 64,512 per IP | +| 4 | 258,048 | 64,512 per IP | +| 8 | 516,096 | 64,512 per IP | +| 16 (max) | 1,032,192 | 64,512 per IP | + +### SNAT Timer Behavior + +| Scenario | Timer | Notes | +|----------|-------|-------| +| TCP idle timeout | 4-120 min (configurable) | Reset on data transfer | +| TCP FIN | 120 seconds | After FIN sent | +| TCP RST | 10 seconds | Immediate port reclaim after RST | +| UDP idle timeout | 4 minutes | Not configurable | + +## References + +- [SNAT Fundamentals](references/snat-fundamentals.md) +- [NAT Gateway Metrics](references/nat-gateway-metrics.md) +- [Troubleshoot SNAT Exhaustion](references/troubleshoot-snat.md) diff --git a/plugin/skills/azure-nat-gateway/references/nat-gateway-metrics.md b/plugin/skills/azure-nat-gateway/references/nat-gateway-metrics.md new file mode 100644 index 000000000..f49b90172 --- /dev/null +++ b/plugin/skills/azure-nat-gateway/references/nat-gateway-metrics.md @@ -0,0 +1,250 @@ +# NAT Gateway Metrics and Monitoring + +## Key NAT Gateway Metrics + +Azure NAT Gateway exposes the following metrics through Azure Monitor. All metrics are available in the `Microsoft.Network/natGateways` resource provider namespace. + +### SNATConnectionCount + +- **Description**: Total number of active SNAT connections at a point in time. This is the concurrently active connection count — not a cumulative total. +- **Unit**: Count +- **Aggregation**: Sum, Max, Avg +- **Why it matters**: This is your primary capacity indicator. Each public IP supports 64,512 SNAT ports. When SNATConnectionCount approaches that ceiling (or `64,512 × number_of_public_IPs`), new connections will fail. +- **Dimensions**: Protocol (TCP/UDP), Connection State (Attempted, Active, Failed, Timed Out) +- **Healthy range**: Below 80% of available port capacity. For 1 public IP, keep below ~51,600 sustained. + +Use the **Connection State** dimension to break down connections: +- **Attempted**: New connections initiated — a rising trend indicates growing demand. +- **Active**: Currently established connections consuming SNAT ports. +- **Failed**: Connections that could not be established — non-zero values indicate exhaustion or connectivity problems. +- **Timed Out**: Connections closed by idle timeout expiry — high values may indicate the idle timeout is too short or connections are being abandoned. + +### TotalConnectionCount + +- **Description**: Cumulative total of all SNAT connections over a time period, including succeeded and failed. +- **Unit**: Count +- **Aggregation**: Sum +- **Why it matters**: Shows the overall connection rate and throughput of your NAT Gateway. Use this to understand connection velocity — how many connections per minute or hour your workload generates. + +### DroppedPackets + +- **Description**: Number of packets dropped by NAT Gateway. +- **Unit**: Count +- **Aggregation**: Sum +- **Why it matters**: This is the most critical alert metric. Dropped packets typically mean SNAT port exhaustion — NAT Gateway cannot allocate a port for a new connection and drops the SYN packet. Any non-zero value warrants investigation. +- **Common causes**: SNAT exhaustion, NAT Gateway health issues, or packet malformation. + +### ByteCount + +- **Description**: Total number of bytes processed (inbound + outbound) through NAT Gateway. +- **Unit**: Bytes +- **Aggregation**: Sum +- **Why it matters**: Tracks data throughput. Useful for capacity planning and cost estimation. NAT Gateway supports up to 50 Gbps of throughput — if ByteCount trends indicate you are approaching this, consider distributing traffic across multiple subnets with separate NAT Gateways. + +### PacketCount + +- **Description**: Total number of packets processed through NAT Gateway. +- **Unit**: Count +- **Aggregation**: Sum +- **Why it matters**: Combined with ByteCount, helps identify traffic patterns. A high packet count with low byte count suggests many small requests (e.g., API calls), while low packet count with high bytes suggests bulk transfers. + +### DatapathAvailability + +- **Description**: Health of the NAT Gateway data path, expressed as a percentage. +- **Unit**: Percent +- **Aggregation**: Avg, Min +- **Why it matters**: Indicates whether NAT Gateway is healthy and processing traffic. A value of 100% means fully operational. Values below 100% indicate a platform issue affecting NAT Gateway — this is not caused by customer configuration and should be reported to Azure support. +- **Healthy value**: 100% at all times. Any sustained drop below 100% is a platform incident. + +## Setting Up Metric Alerts + +### Alert 1: SNAT Exhaustion Warning (DroppedPackets) + +This is the highest-priority alert. Any dropped packets indicate connections are failing. + +```bash +# Create an action group for notifications +az monitor action-group create -g MyRG -n NATGatewayAlerts \ + --short-name NATAlerts \ + --action email admin admin@contoso.com + +# Create alert for dropped packets > 0 +az monitor metrics alert create -g MyRG -n NATGateway-DroppedPackets \ + --scopes /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --condition "total DroppedPackets > 0" \ + --window-size 5m \ + --evaluation-frequency 1m \ + --severity 1 \ + --action NATGatewayAlerts \ + --description "NAT Gateway is dropping packets — possible SNAT exhaustion" +``` + +### Alert 2: High SNAT Connection Count + +Warn before reaching capacity. For a single public IP, alert at 80% (51,600 connections). + +```bash +az monitor metrics alert create -g MyRG -n NATGateway-HighSNAT \ + --scopes /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --condition "max SNATConnectionCount > 51600" \ + --window-size 5m \ + --evaluation-frequency 1m \ + --severity 2 \ + --action NATGatewayAlerts \ + --description "SNAT connections exceeding 80% of single-IP capacity" +``` + +Adjust the threshold based on your public IP count: `threshold = 64,512 × number_of_IPs × 0.8`. + +### Alert 3: Datapath Availability Drop + +```bash +az monitor metrics alert create -g MyRG -n NATGateway-DatapathHealth \ + --scopes /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --condition "avg DatapathAvailability < 100" \ + --window-size 5m \ + --evaluation-frequency 1m \ + --severity 1 \ + --action NATGatewayAlerts \ + --description "NAT Gateway datapath availability degraded — possible platform issue" +``` + +### Alert 4: Failed Connection Rate + +Alert when failed connections exceed a threshold over a 5-minute window. + +```bash +az monitor metrics alert create -g MyRG -n NATGateway-FailedConnections \ + --scopes /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --condition "total SNATConnectionCount > 100" \ + --condition-dimension "ConnectionState=Failed" \ + --window-size 5m \ + --evaluation-frequency 1m \ + --severity 2 \ + --action NATGatewayAlerts \ + --description "High rate of failed SNAT connections" +``` + +## Dashboard Creation + +Create an Azure Monitor workbook or dashboard with these panels for comprehensive NAT Gateway visibility: + +### Recommended Dashboard Panels + +1. **SNAT Connection Count (Time Chart)**: Line chart showing SNATConnectionCount over time, with a horizontal threshold line at 80% of capacity. Split by Connection State dimension. +2. **Dropped Packets (Bar Chart)**: Bar chart of DroppedPackets per 5-minute interval. Any bars indicate problems. +3. **Datapath Availability (Gauge)**: Single-value gauge showing current DatapathAvailability. Green at 100%, red below. +4. **Connection Rate (Time Chart)**: TotalConnectionCount as a rate (connections per minute) to see demand trends. +5. **Throughput (Time Chart)**: ByteCount as a rate (MB/s) to monitor data throughput. +6. **Packet Rate (Time Chart)**: PacketCount per minute to identify traffic spikes. + +### Azure CLI: Query Metrics for Dashboard Data + +```bash +# SNAT connections over the last hour, 5-minute intervals +az monitor metrics list \ + --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --metric SNATConnectionCount \ + --interval PT5M \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ + --aggregation Maximum + +# Dropped packets over the last 24 hours +az monitor metrics list \ + --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --metric DroppedPackets \ + --interval PT1H \ + --start-time $(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ) \ + --aggregation Total + +# Throughput over the last hour +az monitor metrics list \ + --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --metric ByteCount \ + --interval PT5M \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ + --aggregation Total +``` + +## Diagnostic Logs + +NAT Gateway supports diagnostic settings to route logs to Log Analytics, Storage, or Event Hubs. + +### Enable Diagnostic Settings + +```bash +az monitor diagnostic-settings create \ + --name NATGatewayDiagnostics \ + --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --workspace /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.OperationalInsights/workspaces/MyWorkspace \ + --metrics '[{"category":"AllMetrics","enabled":true,"retentionPolicy":{"enabled":true,"days":90}}]' +``` + +### Log Analytics Integration + +Once diagnostic settings are configured, query NAT Gateway metrics in Log Analytics using Kusto Query Language (KQL): + +```kusto +// SNAT connection trends over the last 24 hours +AzureMetrics +| where ResourceProvider == "MICROSOFT.NETWORK" +| where Resource contains "NATGATEWAY" +| where MetricName == "SNATConnectionCount" +| where TimeGenerated > ago(24h) +| summarize MaxConnections = max(Maximum), AvgConnections = avg(Average) by bin(TimeGenerated, 5m) +| order by TimeGenerated desc + +// Detect SNAT exhaustion events (dropped packets) +AzureMetrics +| where ResourceProvider == "MICROSOFT.NETWORK" +| where Resource contains "NATGATEWAY" +| where MetricName == "DroppedPackets" +| where Total > 0 +| project TimeGenerated, Resource, Total +| order by TimeGenerated desc + +// Datapath availability dips +AzureMetrics +| where ResourceProvider == "MICROSOFT.NETWORK" +| where Resource contains "NATGATEWAY" +| where MetricName == "DatapathAvailability" +| where Average < 100 +| project TimeGenerated, Resource, Average +| order by TimeGenerated desc +``` + +## Interpreting Metric Patterns + +### Healthy NAT Gateway + +- **SNATConnectionCount**: Steady or follows predictable business-hours pattern. Well below 80% of capacity. +- **DroppedPackets**: Consistently zero. +- **DatapathAvailability**: Constant 100%. +- **TotalConnectionCount**: Smooth rate matching expected application traffic. + +### SNAT Exhaustion in Progress + +- **SNATConnectionCount**: Plateaus near capacity ceiling (e.g., ~64,000 for 1 IP). +- **DroppedPackets**: Spikes appear, correlating with peak traffic times. +- **TotalConnectionCount**: Connection attempts remain high, but successful connections flatten. +- **Action**: Add more public IPs immediately. Investigate application connection management. + +### Connection Leak + +- **SNATConnectionCount**: Steadily climbs over hours or days without corresponding workload increase. +- **DroppedPackets**: Eventually begins as count approaches capacity. +- **TotalConnectionCount**: New connections continue, but active connections never decrease. +- **Action**: Application-level investigation — look for unclosed sockets, missing connection pool cleanup, or HTTP clients not being disposed. + +### Platform Health Issue + +- **DatapathAvailability**: Drops below 100%, possibly to 0%. +- **All other metrics**: May drop to zero or show erratic behavior. +- **DroppedPackets**: May spike if traffic is partially flowing. +- **Action**: Check Azure Status page. Open a support ticket with Microsoft. This is a platform issue, not a configuration problem. + +### Intermittent Exhaustion + +- **SNATConnectionCount**: Spikes briefly to near-capacity during peak periods, then recovers. +- **DroppedPackets**: Small numbers during spike periods only. +- **Action**: May be acceptable if brief and infrequent. Consider adding a public IP for headroom, or optimize application connection patterns for peak periods. diff --git a/plugin/skills/azure-nat-gateway/references/snat-fundamentals.md b/plugin/skills/azure-nat-gateway/references/snat-fundamentals.md new file mode 100644 index 000000000..d46f07fcc --- /dev/null +++ b/plugin/skills/azure-nat-gateway/references/snat-fundamentals.md @@ -0,0 +1,188 @@ +# SNAT Fundamentals for Azure NAT Gateway + +## What Is SNAT (Source Network Address Translation)? + +SNAT is the process of rewriting the source IP address and source port of an outbound packet so that traffic from a private IP address can reach the public internet and return responses can be routed back to the correct origin. + +When a virtual machine with private IP `10.0.1.4` sends a request to `api.example.com`, it cannot use its private address on the public internet. A SNAT device — such as NAT Gateway — replaces the source IP with a public IP (e.g., `20.50.100.10`) and assigns a unique source port. When the response arrives at `20.50.100.10:`, NAT Gateway translates it back to `10.0.1.4` and delivers it to the VM. + +This translation is stateful: NAT Gateway maintains a connection tracking table that maps each outbound flow to its original private source. + +## SNAT Port Tuples + +Every outbound connection through SNAT is identified by a **5-tuple**: + +| Element | Description | +|---------|-------------| +| Source IP | The public IP assigned by NAT Gateway | +| Source port | The ephemeral port allocated by NAT Gateway (1,024–65,535) | +| Destination IP | The remote server's IP address | +| Destination port | The remote server's port (e.g., 443 for HTTPS) | +| Protocol | TCP or UDP | + +Two connections can share the same source port **only if** they differ in at least one other tuple element. For example, connections to `10.10.10.1:443` and `10.10.10.2:443` from the same public IP can reuse the same source port because the destination IP differs. + +This means the effective port capacity depends on how many unique destinations your workload communicates with. A workload connecting to many different destinations can reuse ports aggressively, while a workload funneling all traffic to a single destination IP and port consumes one unique port per connection. + +## NAT Gateway SNAT Behavior + +### On-Demand Port Allocation + +NAT Gateway allocates SNAT ports **on demand** — ports are assigned when a connection is initiated and released when the connection terminates. There is no pre-allocation or static assignment per VM instance. + +This is fundamentally different from Load Balancer SNAT: + +| Behavior | NAT Gateway | Load Balancer SNAT | +|----------|-------------|-------------------| +| Allocation model | On-demand per flow | Pre-allocated per backend instance | +| Port pool | 64,512 per public IP, shared across all VMs on subnet | Divided among backend pool members | +| Scaling impact | Adding VMs does not reduce per-VM ports | Adding VMs reduces ports per instance | +| Unused ports | Available to any VM on the subnet | Wasted if instance is idle | + +With NAT Gateway, all 64,512 ports on a public IP are available to any VM on the associated subnet. A single busy VM can consume many ports while idle VMs consume none. This dynamic sharing makes NAT Gateway far more efficient for variable workloads. + +### Port Inventory per Public IP + +Each public IP attached to NAT Gateway provides **64,512 SNAT ports** (ephemeral port range 1,024–65,535). With the maximum of 16 public IP addresses: + +- **1 public IP**: 64,512 ports +- **4 public IPs**: 258,048 ports +- **16 public IPs**: 1,032,192 ports + +You can also attach a **public IP prefix** instead of individual IPs. A /28 prefix provides 16 IPs and the maximum 1,032,192 ports, with the advantage of a contiguous IP range for firewall allowlisting. + +## Connection Flow Walkthrough + +Here is the complete path of an outbound connection through NAT Gateway: + +### Outbound Path (VM → Internet) + +1. **VM initiates connection**: Application on VM `10.0.1.4` opens a TCP connection to `api.example.com:443`. +2. **Subnet routing**: The subnet has NAT Gateway associated. Azure networking routes the outbound packet to NAT Gateway instead of the default internet path. +3. **SNAT translation**: NAT Gateway selects a public IP (e.g., `20.50.100.10`), allocates an available source port (e.g., `48372`), and rewrites the packet source to `20.50.100.10:48372`. +4. **Connection tracking**: NAT Gateway creates a flow entry: `10.0.1.4:54210 ↔ 20.50.100.10:48372 → api.example.com:443`. +5. **Packet forwarded to internet**: The translated packet reaches `api.example.com` with source `20.50.100.10:48372`. + +### Return Path (Internet → VM) + +6. **Response arrives**: `api.example.com` responds to `20.50.100.10:48372`. +7. **Reverse translation**: NAT Gateway looks up port `48372` in its flow table, finds it maps to `10.0.1.4:54210`. +8. **Packet delivered**: The response is rewritten with destination `10.0.1.4:54210` and delivered to the VM. + +### NAT Gateway Supersedes Other Outbound Methods + +When NAT Gateway is associated with a subnet, it takes priority over all other outbound configurations: + +- Load Balancer outbound rules on that subnet are bypassed for internet traffic. +- Instance-level public IPs on VMs are not used for outbound (but still work for inbound). +- Default outbound access is overridden. + +This means you get a single, predictable outbound IP (or set of IPs) for the entire subnet. + +## Timer Behavior for TCP and UDP + +### TCP Idle Timeout + +- **Configurable**: 4 to 120 minutes (default: 4 minutes). +- **Reset on activity**: Any data transfer in either direction resets the idle timer. +- **Effect of expiry**: If the idle timer expires, NAT Gateway sends a TCP RST to both sides and reclaims the port. + +Set idle timeout based on your workload. Long-lived connections (database connections, WebSocket proxies) may need higher values. Short-lived HTTP request/response patterns work well with the 4-minute default. + +### TCP FIN Timer + +- **Duration**: 120 seconds after a FIN is sent. +- **Purpose**: Allows the connection to complete its four-way TCP shutdown gracefully. +- **Port reclaim**: The SNAT port is held during this period and released after the timer expires or the full FIN/ACK exchange completes. + +### TCP RST Timer + +- **Duration**: 10 seconds after an RST is sent or received. +- **Purpose**: Brief hold to handle any in-flight packets before reclaiming the port. +- **Behavior**: This is the fastest port reclaim mechanism — an RST immediately signals connection termination. + +### UDP Idle Timeout + +- **Fixed**: 4 minutes — not configurable. +- **No connection state**: UDP is connectionless, so NAT Gateway uses the idle timer as the only mechanism to reclaim ports. +- **Best practice**: Applications using UDP should send periodic keepalives if they need the flow to persist beyond 4 minutes. + +## SNAT Exhaustion + +### What It Looks Like + +SNAT exhaustion occurs when all available ports for a given destination are in use and no new connections can be established. Symptoms include: + +- **Connection timeouts**: New outbound TCP connections fail with timeout errors. +- **HTTP 500 or 502 errors**: Application proxies and API gateways return server errors when they cannot establish backend connections. +- **Intermittent failures**: The problem appears and disappears as ports are allocated and released — busier periods hit the ceiling first. +- **Increased latency**: Connections may succeed but take longer as NAT Gateway waits for port availability. + +### When It Happens + +SNAT exhaustion is most likely when: + +- A workload opens many simultaneous connections to the **same destination IP and port** (e.g., a single database endpoint or API). +- Application code leaks connections — sockets are opened but never properly closed. +- Microservices fan-out: a single request triggers dozens of outbound calls to other services. +- Retry storms: aggressive retries without exponential backoff create a flood of new connections. +- Low idle timeout with long-lived connections causes premature port reclaim followed by immediate reconnect pressure. + +## Default Outbound Access Retirement + +Azure is retiring default outbound access for VMs and VMSS: + +- **New deployments** (after September 30, 2025): VMs created without explicit outbound connectivity will have no internet access by default. +- **Existing deployments**: Continue to work but are not recommended and receive no SLA. +- **Recommended action**: Associate a NAT Gateway, assign a VM-level public IP, or configure Load Balancer outbound rules. + +NAT Gateway is the preferred migration path because it provides reliable, scalable, and predictable outbound access with a guaranteed SLA. + +## Comparison: Outbound SNAT Methods + +| Characteristic | NAT Gateway | Load Balancer SNAT | VM Public IP | Default Outbound | +|---------------|-------------|-------------------|-------------|-----------------| +| Port allocation | On-demand, shared pool | Pre-allocated per instance | All 64K ports per VM | Platform-managed | +| Ports per public IP | 64,512 | Max 1,024 per instance | 65,535 | Unknown/variable | +| Max public IPs | 16 | Multiple (varies) | 1 per VM | N/A | +| Static IP | Yes | Yes | Yes | No (unpredictable) | +| Zone resiliency | Yes (spans all zones) | Zone-dependent | Zone-dependent | No guarantee | +| SLA | Yes (99.99% for zonal deployment) | Part of LB SLA | Yes | No SLA | +| Inbound support | No | Yes | Yes | No | +| Subnet-level config | Yes | Backend pool | Per-VM | Implicit | +| Recommended for | All outbound workloads | Combined inbound + outbound | Single VMs, dev/test | Not recommended | + +## Port Reuse and Recycling + +NAT Gateway recycles ports as quickly as possible: + +1. **Immediate reuse to different destinations**: A port released from one destination can be immediately used for a connection to a different destination IP or port — because the 5-tuple differs. +2. **Same-destination reuse**: A port used for a specific destination cannot be reused for the same destination until the relevant timer expires (idle timeout, FIN timer, or RST timer). +3. **TCP RST is fastest**: If your application can cleanly RST connections, ports are available in 10 seconds. FIN-based closure holds the port for up to 120 seconds. +4. **No TIME_WAIT buildup**: Unlike OS-level SNAT, NAT Gateway manages its own timers independently of the VM's TCP stack. The VM may show TIME_WAIT sockets, but NAT Gateway tracks port lifecycle separately. + +## Best Practices + +### Connection Management + +- **Use connection pooling**: HTTP connection pools (keep-alive), database connection pools, and gRPC channel reuse dramatically reduce SNAT port consumption. +- **Close connections properly**: Ensure application code closes sockets in finally blocks or uses `using` / `with` patterns to prevent leaks. +- **Implement exponential backoff**: Retry storms are a leading cause of SNAT exhaustion — back off exponentially with jitter. + +### Idle Timeout Configuration + +- **Match your workload**: Set idle timeout to slightly longer than your application's expected idle period between requests on a persistent connection. +- **Don't set it to 120 minutes by default**: Excessively long timeouts hold ports unnecessarily and delay reclamation of abandoned connections. +- **Use TCP keepalives**: If connections must persist for long periods, enable TCP keepalives at the application or OS level to reset the idle timer without increasing the NAT Gateway idle timeout. + +### Scaling + +- **Start with 1 public IP**: 64,512 ports is sufficient for most workloads. Monitor `SNATConnectionCount` and `DroppedPackets` before adding more. +- **Add IPs proactively**: If SNAT usage consistently exceeds 50% of capacity, add another public IP before exhaustion occurs. +- **Use public IP prefixes**: If you need many IPs, a prefix provides a contiguous range that simplifies firewall rules on the remote side. + +### Monitoring + +- **Alert on DroppedPackets > 0**: Any dropped packets indicate SNAT exhaustion or NAT Gateway issues — this should trigger investigation. +- **Track SNATConnectionCount trends**: A steadily rising connection count without a corresponding workload increase suggests connection leaks. +- **Monitor DatapathAvailability**: Values below 100% indicate NAT Gateway health issues that need immediate attention. diff --git a/plugin/skills/azure-nat-gateway/references/troubleshoot-snat.md b/plugin/skills/azure-nat-gateway/references/troubleshoot-snat.md new file mode 100644 index 000000000..f9041fe7c --- /dev/null +++ b/plugin/skills/azure-nat-gateway/references/troubleshoot-snat.md @@ -0,0 +1,397 @@ +# Troubleshoot NAT Gateway and SNAT Exhaustion + +## Symptoms of SNAT Exhaustion + +SNAT exhaustion occurs when all available SNAT ports on your NAT Gateway are in use and new outbound connections cannot be established. Recognizing the symptoms is the first step to resolution. + +### Application-Level Symptoms + +- **Intermittent connection timeouts**: Outbound HTTP requests or database connections sporadically fail with timeout errors. The failures are not constant — they correlate with traffic volume. +- **HTTP 500 or 502 errors**: If your application proxies outbound requests (e.g., an API gateway calling a backend), SNAT failures surface as server errors to clients. +- **Socket connection refused or reset**: TCP SYN packets are dropped silently — the application sees either a timeout (no response) or a connection reset. +- **Increased latency before failure**: Some connections succeed but take longer as NAT Gateway struggles to find available ports. +- **Partial outages**: Some VMs on the subnet experience failures while others work — this happens when a few VMs are consuming a disproportionate share of the SNAT port pool. + +### Infrastructure-Level Symptoms + +- **DroppedPackets metric > 0**: The most definitive indicator. NAT Gateway is actively dropping packets due to port exhaustion. +- **SNATConnectionCount near capacity**: Active connections plateauing near `64,512 × number_of_public_IPs`. +- **Failed connection state increasing**: The SNATConnectionCount metric with Connection State = Failed dimension shows rising failures. + +## Diagnostic Steps + +Follow these steps in order to diagnose NAT Gateway outbound connectivity problems. + +### Step 1: Check NAT Gateway Metrics + +Start with Azure Monitor metrics to confirm whether SNAT exhaustion is the cause. + +```bash +# Check for dropped packets in the last hour +az monitor metrics list \ + --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --metric DroppedPackets \ + --interval PT5M \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ + --aggregation Total + +# Check active SNAT connections +az monitor metrics list \ + --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --metric SNATConnectionCount \ + --interval PT5M \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ + --aggregation Maximum + +# Check datapath availability +az monitor metrics list \ + --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/natGateways/MyNATGateway \ + --metric DatapathAvailability \ + --interval PT5M \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ + --aggregation Average +``` + +**Interpretation**: +- DroppedPackets > 0 → SNAT exhaustion confirmed. +- SNATConnectionCount near capacity → exhaustion imminent or active. +- DatapathAvailability < 100% → platform issue, not SNAT exhaustion. Contact Azure support. + +### Step 2: Verify NAT Gateway Subnet Association + +A common misconfiguration is that the NAT Gateway exists but is not associated with the subnet where the VMs reside. + +```bash +# Show NAT Gateway and its associated subnets +az network nat gateway show -g MyRG -n MyNATGateway --query '{name:name, subnets:subnets[].id}' -o json + +# Show subnet configuration to verify NAT Gateway association +az network vnet subnet show -g MyRG --vnet-name MyVNet -n AppSubnet --query '{name:name, natGateway:natGateway.id}' -o json +``` + +If the subnet's `natGateway` field is null or points to the wrong NAT Gateway, traffic is not flowing through it. + +```bash +# Associate NAT Gateway with the subnet +az network vnet subnet update -g MyRG --vnet-name MyVNet -n AppSubnet --nat-gateway MyNATGateway +``` + +### Step 3: Check Public IP Count and Calculate Available Ports + +```bash +# List public IPs attached to the NAT Gateway +az network nat gateway show -g MyRG -n MyNATGateway \ + --query '{publicIps:publicIpAddresses[].id, publicPrefixes:publicIpPrefixes[].id}' -o json + +# Count available ports +# Each public IP = 64,512 ports +# Each /28 prefix = 16 IPs = 1,032,192 ports +# Each /29 prefix = 8 IPs = 516,096 ports +# Each /30 prefix = 4 IPs = 258,048 ports +# Each /31 prefix = 2 IPs = 129,024 ports +``` + +### Step 4: Review Application Connection Patterns + +SSH into a VM on the subnet and inspect active connections: + +```bash +# Count established outbound connections (Linux) +ss -tn state established | wc -l + +# Group connections by destination +ss -tn state established | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20 + +# Count connections in TIME_WAIT +ss -tn state time-wait | wc -l + +# Check for connections to a single destination (potential hotspot) +ss -tn state established dst 10.0.0.50 | wc -l +``` + +On Windows VMs: + +```powershell +# Count established outbound connections +(Get-NetTCPConnection -State Established).Count + +# Group by remote address +Get-NetTCPConnection -State Established | Group-Object RemoteAddress | Sort-Object Count -Descending | Select-Object -First 20 +``` + +## Port Inventory Calculation + +Estimate the SNAT ports your workload requires: + +``` +Required ports = (Connections per second) × (Average connection duration in seconds) +``` + +**Example**: An application makes 500 HTTP requests/sec to an API. Each request takes 200ms. + +``` +Required ports = 500 × 0.2 = 100 concurrent ports +``` + +This is well within a single public IP. But if the application has connection leaks and connections are never closed: + +``` +Leaked connections over 1 hour = 500/sec × 3,600 sec = 1,800,000 connections +``` + +This would exceed even the maximum 1,032,192 ports — the leaks, not the traffic volume, cause exhaustion. + +**Rule of thumb**: If your calculation says you need more than 50,000 ports per public IP, investigate whether connections are being properly closed before adding more IPs. + +## Common Causes + +### 1. Connection Pool Leaks + +**Symptom**: SNATConnectionCount steadily climbs over hours, never decreasing. + +**Cause**: Application code opens HTTP clients, database connections, or sockets without closing them. Common in languages with garbage collection where developers assume connections are cleaned up automatically. + +**Fix**: +```csharp +// BAD: Creates a new HttpClient per request, may not be disposed +var client = new HttpClient(); +var response = await client.GetAsync(url); + +// GOOD: Use IHttpClientFactory (ASP.NET Core) or a shared static instance +private static readonly HttpClient _client = new HttpClient(); +var response = await _client.GetAsync(url); +``` + +```python +# BAD: Session never closed +session = requests.Session() +response = session.get(url) + +# GOOD: Use context manager +with requests.Session() as session: + response = session.get(url) +``` + +### 2. Microservice Fan-Out + +**Symptom**: SNAT spikes correlate with specific API calls that trigger many downstream requests. + +**Cause**: A single inbound request triggers 10-50 outbound calls to microservices. If the API receives 100 requests/sec, that creates 1,000-5,000 outbound connections/sec. + +**Fix**: +- Use internal load balancers and private endpoints instead of going through NAT Gateway for internal service-to-service communication. +- Batch downstream requests where possible. +- Use async/await patterns to limit concurrent outbound connections. + +### 3. Aggressive Retry Without Backoff + +**Symptom**: SNAT spikes during downstream service outages or slow periods. + +**Cause**: When a downstream service is slow or down, the application retries immediately and repeatedly, creating a flood of new connections that consume ports. + +**Fix**: +- Implement exponential backoff with jitter. +- Use circuit breaker patterns (e.g., Polly in .NET, resilience4j in Java). +- Set maximum retry counts. + +### 4. DNS Resolution Creating New Connections + +**Symptom**: High connection turnover even though the application uses connection pooling. + +**Cause**: DNS TTL expiry forces the HTTP client to close existing connections and open new ones to the resolved IP. Short DNS TTLs combined with many clients cause connection churn. + +**Fix**: +- Configure appropriate DNS caching at the application level. +- Use connection pooling that supports DNS refresh without closing all connections. + +## Resolution Steps + +### Immediate: Add More Public IPs + +The fastest way to relieve SNAT exhaustion is to attach additional public IPs: + +```bash +# Create and attach a second public IP +az network public-ip create -g MyRG -n NATPublicIP2 --sku Standard --allocation-method Static +az network nat gateway update -g MyRG -n MyNATGateway \ + --public-ip-addresses NATPublicIP NATPublicIP2 + +# Or create a public IP prefix for bulk scaling +az network public-ip prefix create -g MyRG -n NATPrefix --length 30 # 4 IPs +az network nat gateway update -g MyRG -n MyNATGateway --public-ip-prefixes NATPrefix +``` + +Each additional public IP adds 64,512 ports. You can attach up to 16 IPs (1,032,192 total ports). + +> **Important**: Adding IPs is a short-term fix. If connection leaks or poor connection management are the root cause, you will eventually exhaust even 16 IPs. + +### Short-Term: Adjust Idle Timeout + +If connections are idle for long periods and holding ports unnecessarily: + +```bash +# Reduce idle timeout to release ports faster (minimum 4 minutes) +az network nat gateway update -g MyRG -n MyNATGateway --idle-timeout 4 +``` + +If connections are being dropped prematurely and reopened: + +```bash +# Increase idle timeout to prevent premature drops (maximum 120 minutes) +az network nat gateway update -g MyRG -n MyNATGateway --idle-timeout 30 +``` + +### Long-Term: Fix Application Connection Management + +- **Enable HTTP connection pooling**: Ensure `Keep-Alive` headers are set and HTTP clients reuse connections. +- **Use database connection pooling**: Configure minimum and maximum pool sizes appropriate for your workload. +- **Implement connection limits**: Set `MaxConnectionsPerServer` or equivalent to prevent any single destination from consuming all ports. +- **Close connections explicitly**: Do not rely on garbage collection or finalizers to close network connections. + +### Long-Term: Reduce TIME_WAIT Accumulation + +TCP connections in TIME_WAIT state hold SNAT ports for up to 120 seconds (the FIN timer). On Linux VMs: + +```bash +# Check current TIME_WAIT count +ss -tn state time-wait | wc -l + +# Enable TCP reuse (on the VM, not NAT Gateway) +sudo sysctl -w net.ipv4.tcp_tw_reuse=1 + +# Reduce FIN timeout at the OS level +sudo sysctl -w net.ipv4.tcp_fin_timeout=30 +``` + +> **Note**: These OS settings affect the VM's local TCP stack, not NAT Gateway timers directly. However, faster local cleanup means the application can reconnect sooner using a new SNAT port. + +## Network Watcher Tools + +### Connection Troubleshoot + +Test outbound connectivity from a VM through NAT Gateway: + +```bash +az network watcher test-connectivity \ + --source-resource MyVM \ + --dest-address api.example.com \ + --dest-port 443 \ + --protocol TCP \ + -g MyRG +``` + +This shows whether the connection succeeds, the latency, and the number of hops. If NAT Gateway is not in the path, the subnet association is likely missing. + +### NSG Flow Logs + +Enable NSG flow logs to see outbound connection patterns: + +```bash +az network watcher flow-log create \ + --location eastus \ + --name FlowLog-AppSubnet \ + --nsg MyNSG \ + --resource-group MyRG \ + --storage-account MyStorageAccount \ + --enabled true \ + --log-version 2 \ + --retention 30 \ + --traffic-analytics true \ + --workspace MyLogAnalyticsWorkspace +``` + +Flow logs show every connection attempt, including source/dest IPs and ports, whether the connection was allowed or denied, and the byte/packet counts. + +### NSG Diagnostics + +Verify that NSG rules are not blocking outbound traffic: + +```bash +az network watcher show-security-group-view --resource-group MyRG --vm MyVM +``` + +Look for rules that deny outbound traffic on port 443, 80, or other ports your application uses. + +## Verifying Outbound IP + +Confirm that traffic is actually flowing through NAT Gateway by checking the outbound IP from a VM: + +```bash +# From a Linux VM +curl -s ifconfig.me +curl -s ipinfo.io/ip +curl -s checkip.amazonaws.com + +# From a Windows VM +Invoke-RestMethod -Uri "https://ifconfig.me" +(Invoke-WebRequest -Uri "https://api.ipify.org").Content +``` + +The returned IP should match one of the public IPs attached to your NAT Gateway. If it returns a different IP: + +- NAT Gateway is not associated with the VM's subnet. +- The VM has an instance-level public IP that is being used for outbound (Note: NAT Gateway should supersede this, but verify the configuration). +- A User-Defined Route (UDR) is overriding the path and sending traffic through an NVA or VPN instead of NAT Gateway. + +## Troubleshooting NAT Gateway Not Working + +### Problem: NAT Gateway Associated But No Outbound Connectivity + +**Check 1: Subnet association is correct** +```bash +az network vnet subnet show -g MyRG --vnet-name MyVNet -n AppSubnet --query natGateway.id -o tsv +``` +Verify the output matches your NAT Gateway resource ID. + +**Check 2: NSG is not blocking outbound** +```bash +# List NSG rules on the subnet and NIC +az network nsg rule list --nsg-name MyNSG -g MyRG -o table +``` +Ensure no deny rules block outbound traffic on the required ports. The default NSG allows all outbound, but custom rules may override this. + +**Check 3: UDR is not redirecting traffic** +```bash +az network route-table route list --route-table-name MyRouteTable -g MyRG -o table +``` +If a route sends `0.0.0.0/0` to a virtual appliance or VPN gateway, traffic bypasses NAT Gateway. NAT Gateway only handles traffic that Azure networking routes to the internet gateway. A UDR with next-hop `Internet` works with NAT Gateway; a UDR with next-hop `VirtualAppliance` or `VirtualNetworkGateway` does not. + +**Check 4: Public IP is Standard SKU** +```bash +az network public-ip show -g MyRG -n NATPublicIP --query sku.name -o tsv +``` +Must be `Standard`. Basic SKU public IPs cannot be used with NAT Gateway. + +**Check 5: NAT Gateway is in a healthy state** +```bash +az network nat gateway show -g MyRG -n MyNATGateway --query provisioningState -o tsv +``` +Must be `Succeeded`. If it shows `Failed` or `Updating`, the NAT Gateway may be in an error state. + +### Problem: Intermittent Outbound Failures Despite Low SNAT Usage + +If SNATConnectionCount is well below capacity but connections still fail intermittently: + +- **Check destination-side limits**: The remote server may be rate-limiting your IP or rejecting connections. +- **Check DNS resolution**: If DNS fails, the application cannot resolve the destination and the connection never reaches NAT Gateway. +- **Check TCP MSS / MTU issues**: Path MTU discovery failures can cause packets to be silently dropped. Ensure ICMP is not blocked by NSG rules. +- **Check application timeouts**: If the application timeout is shorter than NAT Gateway's processing time under load, the app may give up before the connection is established. + +### Problem: VM Has No Outbound Connectivity At All + +If a VM has zero outbound connectivity (not intermittent — completely blocked): + +1. Verify the VM's subnet has NAT Gateway associated. +2. Verify the NSG allows outbound traffic (check both subnet NSG and NIC NSG). +3. Verify no UDR is sending all traffic to a black hole (non-existent NVA). +4. Verify the VM has a NIC that is connected and the NIC is in a `Succeeded` provisioning state. +5. Verify the VM itself is running and the guest OS network stack is healthy. +6. Test with `ping` to the Azure metadata endpoint `169.254.169.254` — this does not go through NAT Gateway and verifies basic VM networking is functional. + +```bash +# Test Azure metadata (does not use NAT Gateway) +curl -s -H "Metadata:true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | head -c 100 + +# If this works but internet fails, the issue is in the outbound path (NAT Gateway, NSG, or UDR) +# If this also fails, the issue is with the VM's basic networking (NIC, subnet, vnet) +``` diff --git a/plugin/skills/azure-network-architecture/SKILL.md b/plugin/skills/azure-network-architecture/SKILL.md new file mode 100644 index 000000000..e5f6c1af2 --- /dev/null +++ b/plugin/skills/azure-network-architecture/SKILL.md @@ -0,0 +1,125 @@ +--- +name: azure-network-architecture +description: "Network design guidance and service selection router. Helps users choose the right Azure networking services, design hub-and-spoke topologies, plan IP addressing, and implement network segmentation. WHEN: design my network, network architecture, hub and spoke, which load balancer, compare load balancers, IP address plan, landing zone networking, network segmentation, network topology, multi-region network, choose between VPN and ExpressRoute. DO NOT USE FOR: configuring a specific service (use the service-specific skill), troubleshooting (use azure-network-troubleshooter)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Network Architecture Guide + +You are the Azure network design advisor. You help users choose the right networking services, design network topologies, plan IP addressing, and implement segmentation strategies. You present trade-offs, ask clarifying questions, and guide users to the right architecture for their requirements. + +## When to Use + +Activate this skill when the user needs help with: + +- **Service selection:** "Which load balancer should I use?", "VPN or ExpressRoute?", "Front Door vs Application Gateway?" +- **Topology design:** "Hub-and-spoke", "network topology", "multi-region network", "landing zone" +- **IP planning:** "IP address plan", "CIDR sizing", "avoid overlaps" +- **Segmentation:** "Network segmentation", "micro-segmentation", "zero trust networking" +- **General design:** "Design my network", "network architecture" + +## Rules + +1. **Always ask clarifying questions before recommending.** You need to understand workload requirements, scale, compliance needs, and budget constraints before suggesting an architecture. +2. **Present trade-offs explicitly.** Every design decision has pros and cons — state them clearly. +3. **Never recommend a service without understanding the requirements.** "Use Front Door" is wrong if the user has a non-HTTP workload. +4. **Start with the simplest architecture that meets requirements.** Don't over-engineer. A single VNet with subnets may be the right answer. +5. **Reference Azure Well-Architected Framework** networking guidance when relevant. +6. **Cross-reference service-specific skills** for implementation details. This skill handles the "what" and "why" — service skills handle the "how." +7. **Use official Microsoft decision trees** when available (load balancer selection, hybrid connectivity). + +## Questions to Ask + +Before making any recommendation, gather: + +| Question | Why It Matters | +|----------|---------------| +| What workloads will run on this network? | Determines service selection and segmentation | +| How many environments? (dev/staging/prod) | Affects VNet/subscription topology | +| Do you need hybrid connectivity to on-premises? | VPN vs ExpressRoute decision | +| What are your latency requirements? | Affects region selection and connectivity type | +| Is this internet-facing, internal, or both? | Load balancer and firewall selection | +| What compliance requirements apply? | Affects data residency, encryption, and isolation | +| What's your expected scale? (VMs, subnets, regions) | IP planning and topology decisions | +| What's your budget constraint? | Affects service tier and redundancy level | + +## Routing Table + +Use this table to route the user's design question to the appropriate reference doc: + +| User Question | Route To | Reference Doc | +|--------------|----------|---------------| +| "Which load balancer should I use?" | Load balancer decision tree | [lb-selection-guide.md](references/lb-selection-guide.md) | +| "Compare load balancers" | Load balancer decision tree | [lb-selection-guide.md](references/lb-selection-guide.md) | +| "Front Door vs Application Gateway" | Load balancer decision tree | [lb-selection-guide.md](references/lb-selection-guide.md) | +| "Hub-and-spoke design" | Hub-spoke topology | [hub-spoke-design.md](references/hub-spoke-design.md) | +| "Network topology" | Hub-spoke topology | [hub-spoke-design.md](references/hub-spoke-design.md) | +| "Virtual WAN vs hub-spoke" | Hub-spoke topology | [hub-spoke-design.md](references/hub-spoke-design.md) | +| "IP address plan" | IP planning guide | [ip-planning.md](references/ip-planning.md) | +| "CIDR sizing" | IP planning guide | [ip-planning.md](references/ip-planning.md) | +| "Avoid IP overlaps" | IP planning guide | [ip-planning.md](references/ip-planning.md) | +| "Landing zone networking" | Landing zone design | [landing-zone-networking.md](references/landing-zone-networking.md) | +| "Azure landing zone" | Landing zone design | [landing-zone-networking.md](references/landing-zone-networking.md) | +| "Network segmentation" | Segmentation strategies | [segmentation.md](references/segmentation.md) | +| "Zero trust networking" | Segmentation strategies | [segmentation.md](references/segmentation.md) | +| "Micro-segmentation" | Segmentation strategies | [segmentation.md](references/segmentation.md) | +| "VPN or ExpressRoute?" | Hybrid connectivity selection | [hybrid-connectivity-selection.md](references/hybrid-connectivity-selection.md) | +| "Choose between VPN and ExpressRoute" | Hybrid connectivity selection | [hybrid-connectivity-selection.md](references/hybrid-connectivity-selection.md) | +| "Multi-region network" | Hub-spoke + landing zone | [hub-spoke-design.md](references/hub-spoke-design.md), [landing-zone-networking.md](references/landing-zone-networking.md) | +| "Design my network" | Start with requirements questions, then route based on answers | Multiple references | + +## Design Principles + +### 1. Least Privilege Network Access +- Default-deny with NSGs and Azure Firewall +- Use Private Endpoints for PaaS services +- Minimize public IP exposure + +### 2. Defense in Depth +- Layer security: NSGs → Azure Firewall → WAF → DDoS Protection +- Each layer addresses different threat vectors + +### 3. Scalability +- Plan IP address space for 5x current needs +- Use VNet peering for horizontal scaling +- Consider Virtual WAN for 50+ VNets + +### 4. Resiliency +- Zone-redundant deployments for production +- Multi-region for disaster recovery +- Redundant hybrid connectivity (dual ExpressRoute circuits or ExpressRoute + VPN) + +### 5. Observability +- Enable Network Watcher in every region +- NSG flow logs for traffic analysis +- Connection Monitor for proactive alerting + +## Service Skills Cross-Reference + +After determining the architecture, hand off to the appropriate service skill for implementation: + +| Design Decision | Implementation Skill | +|----------------|---------------------| +| Hub VNet with Azure Firewall | `azure-firewall`, `azure-virtual-network` | +| VPN connectivity to on-premises | `azure-vpn-gateway` | +| ExpressRoute connectivity | `azure-expressroute` | +| Virtual WAN deployment | `azure-virtual-wan` | +| Internal load balancing | `azure-load-balancer` | +| Web application delivery | `azure-application-gateway` | +| Global HTTP load balancing | `azure-front-door` | +| DNS architecture | `azure-dns` | +| Private connectivity to PaaS | `azure-private-link` | +| DDoS protection | `azure-ddos-protection` | +| WAF rules and policies | `azure-waf` | +| Network monitoring | `azure-network-watcher` | +| Network governance at scale | `azure-vnet-manager` | + +## Further Reading + +- [Azure networking documentation](https://learn.microsoft.com/azure/networking/) +- [Azure Well-Architected Framework — Networking](https://learn.microsoft.com/azure/well-architected/networking/) +- [Cloud Adoption Framework — Network topology](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/define-an-azure-network-topology) +- [Azure architecture center — Networking](https://learn.microsoft.com/azure/architecture/networking/) diff --git a/plugin/skills/azure-network-architecture/references/hub-spoke-design.md b/plugin/skills/azure-network-architecture/references/hub-spoke-design.md new file mode 100644 index 000000000..0bb5b5f05 --- /dev/null +++ b/plugin/skills/azure-network-architecture/references/hub-spoke-design.md @@ -0,0 +1,252 @@ +# Hub-and-Spoke Network Design + +Guide for designing hub-and-spoke network topologies in Azure using VNet peering or Azure Virtual WAN. + +## Architecture Overview + +Hub-and-spoke is the recommended topology for most Azure deployments. A central **hub VNet** hosts shared services (firewall, VPN/ExpressRoute gateway, DNS). **Spoke VNets** host workloads and peer with the hub. + +``` + On-Premises + │ + VPN / ExpressRoute + │ + ┌────────┴────────┐ + │ Hub VNet │ + │ ┌───────────┐ │ + │ │ Firewall │ │ + │ │ Gateway │ │ + │ │ DNS │ │ + │ │ Bastion │ │ + │ └───────────┘ │ + └───┬────┬────┬───┘ + │ │ │ + Peering│ │ │Peering + │ │ │ + ┌──────┘ │ └──────┐ + ▼ ▼ ▼ + ┌─────────┐ ┌─────────┐ ┌─────────┐ + │ Spoke 1 │ │ Spoke 2 │ │ Spoke 3 │ + │ (Web) │ │ (App) │ │ (Data) │ + └─────────┘ └─────────┘ └─────────┘ +``` + +## VNet Peering Hub-Spoke vs Virtual WAN + +| Criteria | VNet Peering Hub-Spoke | Azure Virtual WAN | +|----------|----------------------|-------------------| +| **Best for** | < 30 spokes, full routing control | 30+ spokes, managed routing, branch connectivity | +| **Hub management** | You manage the hub VNet and all resources | Microsoft manages the hub infrastructure | +| **Transitive routing** | Requires NVA/Firewall + UDRs | Built-in transitive routing | +| **Spoke-to-spoke** | Only through hub NVA (UDRs required) | Automatic via Virtual WAN hub router | +| **On-premises connectivity** | VPN/ER Gateway in hub, gateway transit | Integrated VPN/ER in Virtual WAN hub | +| **Cost** | Pay for individual resources | Hub + routing fee + individual resources | +| **Complexity** | Higher (manage UDRs, peering, NVA HA) | Lower (managed routing and HA) | +| **Customization** | Full control over routing and NVA placement | Less control, follows Virtual WAN routing model | +| **Multi-region** | Peer hub VNets across regions + manage routing | Global transit via interconnected hubs | + +**Decision guide:** +- Choose **VNet peering** if you need full control, have < 30 spokes, and have networking expertise to manage UDRs +- Choose **Virtual WAN** if you have many branches, 30+ spokes, want managed routing, or need rapid multi-region deployment + +## Designing the Hub VNet + +### Hub VNet sizing + +Plan subnets for all shared services. Minimum recommended hub VNet size: `/22` (1,019 usable IPs). + +| Subnet | Purpose | Recommended Size | Notes | +|--------|---------|-----------------|-------| +| GatewaySubnet | VPN/ER Gateway | /27 minimum | Name must be exactly "GatewaySubnet" | +| AzureFirewallSubnet | Azure Firewall | /26 minimum | Name must be exactly "AzureFirewallSubnet" | +| AzureFirewallManagementSubnet | Firewall forced tunneling | /26 | Required if using forced tunneling | +| AzureBastionSubnet | Azure Bastion | /26 minimum | Name must be exactly "AzureBastionSubnet" | +| dns-inbound | DNS Private Resolver inbound | /28 | Dedicated subnet, no other resources | +| dns-outbound | DNS Private Resolver outbound | /28 | Dedicated subnet, no other resources | +| shared-services | Jump boxes, AD DS, monitoring | /24 | Size based on number of shared VMs | + +```bash +# Create hub VNet +az network vnet create \ + --resource-group hub-rg \ + --name hub-vnet \ + --address-prefix 10.0.0.0/22 \ + --location eastus + +# Create hub subnets +az network vnet subnet create -g hub-rg --vnet-name hub-vnet -n GatewaySubnet --address-prefix 10.0.0.0/27 +az network vnet subnet create -g hub-rg --vnet-name hub-vnet -n AzureFirewallSubnet --address-prefix 10.0.0.64/26 +az network vnet subnet create -g hub-rg --vnet-name hub-vnet -n AzureBastionSubnet --address-prefix 10.0.0.128/26 +az network vnet subnet create -g hub-rg --vnet-name hub-vnet -n shared-services --address-prefix 10.0.1.0/24 +``` + +### Spoke VNet sizing + +Each spoke VNet typically gets a `/24` to `/20` depending on workload size. See [ip-planning.md](ip-planning.md) for detailed CIDR guidance. + +## Setting Up Peering + +### Hub-to-spoke peering (with gateway transit) + +```bash +# Create peering from hub to spoke (allow gateway transit) +az network vnet peering create \ + --resource-group hub-rg \ + --name hub-to-spoke1 \ + --vnet-name hub-vnet \ + --remote-vnet /subscriptions//resourceGroups/spoke1-rg/providers/Microsoft.Network/virtualNetworks/spoke1-vnet \ + --allow-vnet-access \ + --allow-forwarded-traffic \ + --allow-gateway-transit + +# Create peering from spoke to hub (use remote gateway) +az network vnet peering create \ + --resource-group spoke1-rg \ + --name spoke1-to-hub \ + --vnet-name spoke1-vnet \ + --remote-vnet /subscriptions//resourceGroups/hub-rg/providers/Microsoft.Network/virtualNetworks/hub-vnet \ + --allow-vnet-access \ + --allow-forwarded-traffic \ + --use-remote-gateways +``` + +### Spoke-to-spoke routing through the hub firewall + +Spokes cannot communicate directly through peering. Route traffic through the hub firewall: + +```bash +# Create route table for spoke subnets +az network route-table create -g spoke1-rg -n spoke1-rt --location eastus + +# Route all non-local traffic to the hub firewall +az network route-table route create \ + -g spoke1-rg \ + --route-table-name spoke1-rt \ + --name to-hub-firewall \ + --address-prefix 10.0.0.0/8 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address + +# Route internet traffic through firewall (optional, for inspection) +az network route-table route create \ + -g spoke1-rg \ + --route-table-name spoke1-rt \ + --name internet-via-firewall \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address + +# Associate route table with spoke subnet +az network vnet subnet update \ + -g spoke1-rg \ + --vnet-name spoke1-vnet \ + --name workload-subnet \ + --route-table spoke1-rt +``` + +## Transitive Routing Options + +When using VNet peering (not Virtual WAN), transitive routing requires explicit configuration: + +| Approach | Complexity | Performance | Cost | +|----------|-----------|-------------|------| +| Azure Firewall in hub | Medium | High (stateful, logged) | Firewall cost | +| NVA in hub (e.g., Palo Alto) | High | Varies by NVA | NVA + VM cost | +| Azure Route Server + NVA | Medium | High (BGP dynamic routing) | Route Server + NVA cost | +| VNet Manager mesh | Low | Highest (direct peering) | VNet Manager fee | + +### Using Azure Virtual Network Manager for spoke mesh + +For spoke-to-spoke traffic that doesn't need firewall inspection: + +```bash +# Create a network group with spokes +az network manager group create \ + --resource-group \ + --network-manager-name \ + --name spoke-group + +# Create a mesh connectivity configuration +az network manager connect-config create \ + --resource-group \ + --network-manager-name \ + --name mesh-config \ + --applies-to-group "[{networkGroupId:, useHubGateway:true, groupConnectivity:DirectlyConnected}]" \ + --connectivity-topology Mesh +``` + +## NVA Placement in the Hub + +If using a third-party NVA instead of Azure Firewall: + +- Deploy the NVA in a **dedicated subnet** in the hub +- Enable **IP forwarding** on the NVA NIC +- Place NVAs behind an **Internal Load Balancer** (Standard SKU, HA ports) for high availability +- Use **two-NIC NVAs** (inside/outside) for security separation +- Set UDRs on all spoke subnets pointing to the ILB frontend IP + +```bash +# Enable IP forwarding on NVA NIC +az network nic update -g hub-rg -n nva-nic --ip-forwarding true + +# Create internal load balancer for NVA HA +az network lb create \ + -g hub-rg -n nva-ilb \ + --sku Standard \ + --vnet-name hub-vnet \ + --subnet nva-subnet \ + --frontend-ip-name nva-frontend \ + --backend-pool-name nva-pool + +# Enable HA ports (all ports, all protocols) +az network lb rule create \ + -g hub-rg --lb-name nva-ilb \ + -n ha-ports-rule \ + --protocol All \ + --frontend-port 0 \ + --backend-port 0 \ + --frontend-ip-name nva-frontend \ + --backend-pool-name nva-pool +``` + +## Multi-Region Hub-Spoke + +For multi-region deployments, create a hub in each region and peer them: + +```bash +# Peer hub VNets across regions (global peering) +az network vnet peering create \ + -g hub-eastus-rg \ + --name hub-eastus-to-hub-westus \ + --vnet-name hub-eastus-vnet \ + --remote-vnet /subscriptions//resourceGroups/hub-westus-rg/providers/Microsoft.Network/virtualNetworks/hub-westus-vnet \ + --allow-vnet-access \ + --allow-forwarded-traffic \ + --allow-gateway-transit +``` + +**Key considerations:** +- Global VNet peering traffic is charged at inter-region rates +- Each hub needs its own gateway for on-premises connectivity (or use ExpressRoute Global Reach) +- DNS must be consistent across regions +- Firewall rules should be managed centrally (use Azure Firewall Manager or Firewall Policy) + +## Design Checklist + +- [ ] Hub VNet sized with room for all shared services subnets +- [ ] Spoke VNet address spaces do not overlap with hub or each other +- [ ] Peering configured with gateway transit and forwarded traffic +- [ ] UDRs on spoke subnets route traffic through hub firewall +- [ ] DNS configured (Private DNS zones linked to hub, DNS forwarding for on-premises) +- [ ] NSGs on all subnets with default-deny inbound +- [ ] Azure Bastion in hub for secure management access +- [ ] Network Watcher enabled in all regions + +## Related Resources + +- [Hub-spoke network topology in Azure](https://learn.microsoft.com/azure/architecture/networking/architecture/hub-spoke) +- [Virtual WAN overview](https://learn.microsoft.com/azure/virtual-wan/virtual-wan-about) +- [Azure Virtual Network Manager overview](https://learn.microsoft.com/azure/virtual-network-manager/overview) +- For IP planning → see [ip-planning.md](ip-planning.md) +- For segmentation → see [segmentation.md](segmentation.md) +- For Virtual WAN configuration → use `azure-virtual-wan` skill diff --git a/plugin/skills/azure-network-architecture/references/hybrid-connectivity-selection.md b/plugin/skills/azure-network-architecture/references/hybrid-connectivity-selection.md new file mode 100644 index 000000000..46aa9ba0f --- /dev/null +++ b/plugin/skills/azure-network-architecture/references/hybrid-connectivity-selection.md @@ -0,0 +1,288 @@ +# Hybrid Connectivity Selection Guide + +Choose between VPN Gateway, ExpressRoute, and Virtual WAN for connecting on-premises networks to Azure. Includes decision criteria, coexistence patterns, and migration paths. + +## Decision Criteria + +| Factor | VPN Gateway | ExpressRoute | Virtual WAN | +|--------|:-----------:|:------------:|:-----------:| +| **Connection type** | Encrypted tunnel over internet | Private dedicated link | Managed hub with VPN + ER | +| **Bandwidth** | Up to 10 Gbps (VpnGw5) | 50 Mbps – 100 Gbps | Aggregate of VPN + ER | +| **Latency** | Variable (internet-dependent) | Low and predictable | Low (ER) or variable (VPN) | +| **SLA** | 99.9% (active-active) or 99.95% (AZ) | 99.95% (single circuit) | 99.95% | +| **Encryption** | IPsec built-in | Not encrypted by default | IPsec for VPN; MACsec for ER Direct | +| **Setup time** | Minutes to hours | Weeks (provider provisioning) | Hours (managed deployment) | +| **Cost** | Low ($150-2,500/month gateway) | Medium-High ($200-12,000/month circuit + gateway) | Medium (hub fee + connections) | +| **Redundancy** | Active-active tunnels | Dual circuits recommended | Built-in HA for VPN and ER | +| **Max sites** | 30-100 S2S tunnels | Per circuit limits | 1,000+ branches | + +## Quick Decision Guide + +``` +Do you need > 1 Gbps bandwidth? +├── YES → Do you need predictable latency? +│ ├── YES → ExpressRoute +│ └── NO → VPN Gateway (VpnGw4/5) or ExpressRoute +└── NO → Is this a production workload? + ├── YES → Is cost the primary concern? + │ ├── YES → VPN Gateway + │ └── NO → ExpressRoute (for reliability) + └── NO → VPN Gateway + +Do you have 10+ branch offices? +├── YES → Virtual WAN (managed branch connectivity) +└── NO → VPN Gateway or ExpressRoute (with hub VNet) + +Do you need connectivity from multiple locations? +├── YES → Is it branch offices with SD-WAN? +│ ├── YES → Virtual WAN +│ └── NO → ExpressRoute with multiple peering locations +└── NO → VPN Gateway (single site) +``` + +## VPN Gateway + +### When to choose VPN Gateway + +- **Budget-constrained** — lowest entry cost for hybrid connectivity +- **Quick setup needed** — can be operational in hours, not weeks +- **Internet-quality latency is acceptable** — suitable for non-real-time workloads +- **Encryption required** — IPsec encryption is built-in +- **Backup connectivity** — commonly paired with ExpressRoute as a failover path +- **Point-to-site** — need remote user VPN access to Azure VNets + +### VPN Gateway SKU comparison + +| SKU | S2S Tunnels | P2S Connections | Throughput | AZ Support | Price (approx) | +|-----|:-----------:|:---------------:|:----------:|:----------:|:--------------:| +| VpnGw1 | 30 | 250 | 650 Mbps | VpnGw1AZ | ~$150/month | +| VpnGw2 | 30 | 500 | 1 Gbps | VpnGw2AZ | ~$350/month | +| VpnGw3 | 30 | 1,000 | 1.25 Gbps | VpnGw3AZ | ~$700/month | +| VpnGw4 | 100 | 5,000 | 5 Gbps | VpnGw4AZ | ~$1,250/month | +| VpnGw5 | 100 | 10,000 | 10 Gbps | VpnGw5AZ | ~$2,500/month | + +```bash +# Create VPN Gateway (zone-redundant) +az network vnet-gateway create \ + -g \ + -n vpn-gateway \ + --vnet \ + --gateway-type Vpn \ + --vpn-type RouteBased \ + --sku VpnGw2AZ \ + --generation Generation2 \ + --location + +# Create site-to-site connection +az network vpn-connection create \ + -g \ + -n onprem-connection \ + --vnet-gateway1 vpn-gateway \ + --local-gateway2 onprem-lng \ + --shared-key \ + --enable-bgp +``` + +### VPN active-active for high availability + +```bash +# Create VPN Gateway with active-active (two tunnels, two public IPs) +az network vnet-gateway create \ + -g \ + -n vpn-gateway \ + --vnet \ + --gateway-type Vpn \ + --vpn-type RouteBased \ + --sku VpnGw2AZ \ + --active-active \ + --public-ip-addresses pip1 pip2 +``` + +## ExpressRoute + +### When to choose ExpressRoute + +- **Predictable, low latency required** — financial trading, real-time applications +- **High bandwidth needed** — data migration, database replication, backup +- **SLA-backed connectivity** — 99.95% uptime with provider SLA stacked on top +- **Accessing Microsoft 365 and Dynamics** (with Microsoft peering) +- **Regulatory requirements** — traffic never traverses the public internet +- **Large-scale data transfer** — avoid internet egress charges + +### ExpressRoute circuit options + +| Option | Bandwidth | Use Case | +|--------|-----------|----------| +| Standard circuit | 50 Mbps – 10 Gbps | Single Azure region connectivity | +| Premium circuit | 50 Mbps – 10 Gbps | Multi-region, global reach, more route prefixes | +| ExpressRoute Direct | 10 Gbps or 100 Gbps | Dedicated port, MACsec encryption, massive bandwidth | +| ExpressRoute Local | Up to 10 Gbps | Discounted when near an ExpressRoute peering location | + +### ExpressRoute peering types + +| Peering Type | Connects To | Route Prefixes | +|-------------|-------------|---------------| +| Azure Private Peering | Azure VNets (private IPs) | VNet address spaces | +| Microsoft Peering | Microsoft 365, Dynamics 365, Azure PaaS (public IPs) | Microsoft service IPs | + +```bash +# Create ExpressRoute circuit +az network express-route create \ + -g \ + -n er-circuit \ + --bandwidth 1000 \ + --peering-location "Silicon Valley" \ + --provider "Equinix" \ + --sku-family MeteredData \ + --sku-tier Premium + +# Create ExpressRoute Gateway +az network vnet-gateway create \ + -g \ + -n er-gateway \ + --vnet \ + --gateway-type ExpressRoute \ + --sku ErGw2AZ \ + --location + +# Connect circuit to gateway +az network vpn-connection create \ + -g \ + -n er-connection \ + --vnet-gateway1 er-gateway \ + --express-route-circuit2 +``` + +### ExpressRoute redundancy best practices + +- **Two circuits from different peering locations** — protects against single provider/location failure +- **ExpressRoute + VPN failover** — VPN as backup when ExpressRoute is down +- **ExpressRoute Global Reach** — connect on-premises sites through Microsoft backbone + +## Virtual WAN + +### When to choose Virtual WAN + +- **Many branch offices** (10+) with SD-WAN integration +- **Want managed routing** — no manual UDRs or peering configuration +- **Need hub transit** — spoke-to-spoke, VPN-to-ExpressRoute transit +- **Multi-region deployment** — global transit network with interconnected hubs +- **Rapid deployment** — automated hub provisioning + +### Virtual WAN vs hub VNet + +| Feature | Virtual WAN | Hub VNet | +|---------|:-----------:|:--------:| +| Spoke-to-spoke routing | Automatic | Manual (UDR + NVA) | +| VPN-to-ER transit | Built-in | Manual configuration | +| Branch SD-WAN integration | 60+ partners | Manual | +| NVA in hub | Limited partners | Full control | +| Custom routing tables | Yes (labels + route tables) | Full UDR control | +| Management overhead | Lower | Higher | +| Cost | Hub fee + routing | Individual resource costs | + +```bash +# Create Virtual WAN +az network vwan create -g -n enterprise-vwan --type Standard + +# Create hub +az network vhub create \ + -g \ + -n hub-eastus \ + --vwan enterprise-vwan \ + --address-prefix 10.0.0.0/23 \ + --location eastus + +# Add VPN Gateway to hub +az network vpn-gateway create \ + -g \ + -n hub-vpn-gw \ + --vhub hub-eastus \ + --location eastus + +# Connect a spoke VNet +az network vhub connection create \ + -g \ + --vhub-name hub-eastus \ + -n spoke1-connection \ + --remote-vnet +``` + +## Coexistence Patterns + +### Pattern 1: ExpressRoute + VPN Backup + +Use ExpressRoute as primary with VPN Gateway as failover. The VPN tunnel activates automatically when ExpressRoute goes down. + +```bash +# Both gateways in the same GatewaySubnet +# ExpressRoute gateway +az network vnet-gateway create -g -n er-gw --vnet hub-vnet --gateway-type ExpressRoute --sku ErGw2AZ + +# VPN gateway (separate public IPs, same subnet) +az network vnet-gateway create -g -n vpn-gw --vnet hub-vnet --gateway-type Vpn --sku VpnGw2AZ +``` + +**Routing behavior:** ExpressRoute routes (BGP) have higher preference. When ER goes down, VPN routes take over. When ER recovers, traffic shifts back automatically. + +### Pattern 2: ExpressRoute for Production + VPN for Dev/Test + +Separate connectivity for different environments: +- ExpressRoute → Hub VNet → Production spokes +- VPN Gateway → Separate VNet → Dev/Test spokes + +### Pattern 3: Dual ExpressRoute Circuits + +Maximum redundancy with two circuits from different providers and peering locations: + +``` +On-premises ──── ER Circuit 1 (Provider A, Location 1) ──── Azure Hub +On-premises ──── ER Circuit 2 (Provider B, Location 2) ──── Azure Hub +``` + +Both circuits connect to the same ExpressRoute gateway. BGP distributes traffic across both. If one fails, the other carries all traffic. + +## Migration Paths + +### VPN → ExpressRoute + +1. Deploy ExpressRoute circuit and gateway alongside existing VPN +2. Configure ExpressRoute private peering +3. Both connections active — ER preferred due to BGP weight +4. Verify all traffic flows over ExpressRoute +5. Decommission VPN (or keep as backup) + +### Hub VNet → Virtual WAN + +1. Deploy Virtual WAN and hub in the same region +2. Connect spoke VNets to Virtual WAN hub +3. Migrate VPN/ER connections to Virtual WAN hub gateways +4. Update DNS and on-premises routing +5. Remove peering to old hub VNet +6. Decommission old hub VNet + +### Single Region → Multi-Region + +1. Deploy second hub (VNet or Virtual WAN hub) in the secondary region +2. Peer hubs (global VNet peering) or connect via Virtual WAN +3. Deploy secondary ExpressRoute circuit at a different peering location +4. Configure geo-redundant VPN if using VPN +5. Set up DNS failover and routing policies + +## Cost Optimization Tips + +- **VPN Gateway:** Use `VpnGw1AZ` for dev/test, scale up only for production +- **ExpressRoute:** Use metered billing if data transfer is low; unlimited for heavy transfer +- **ExpressRoute Local:** Up to 70% discount if your datacenter is near the peering location +- **Virtual WAN:** Only deploy routing infrastructure in regions where you have spokes +- **Reserved capacity:** Commit to 1-year or 3-year reserved pricing for ExpressRoute circuits + +## Related Resources + +- [VPN Gateway overview](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-about-vpngateways) +- [ExpressRoute overview](https://learn.microsoft.com/azure/expressroute/expressroute-introduction) +- [Virtual WAN overview](https://learn.microsoft.com/azure/virtual-wan/virtual-wan-about) +- [ExpressRoute + VPN coexistence](https://learn.microsoft.com/azure/expressroute/expressroute-howto-coexist-resource-manager) +- For VPN configuration → use `azure-vpn-gateway` skill +- For ExpressRoute configuration → use `azure-expressroute` skill +- For Virtual WAN configuration → use `azure-virtual-wan` skill diff --git a/plugin/skills/azure-network-architecture/references/ip-planning.md b/plugin/skills/azure-network-architecture/references/ip-planning.md new file mode 100644 index 000000000..db17aed18 --- /dev/null +++ b/plugin/skills/azure-network-architecture/references/ip-planning.md @@ -0,0 +1,236 @@ +# IP Address Planning Guide + +Plan IP address spaces for Azure virtual networks, avoiding conflicts with on-premises networks, and sizing subnets for current and future growth. + +## Azure IP Address Fundamentals + +### Reserved addresses per subnet + +Azure reserves **5 IP addresses** in every subnet: + +| Address | Purpose | +|---------|---------| +| x.x.x.0 | Network address | +| x.x.x.1 | Default gateway | +| x.x.x.2 | Azure DNS mapping | +| x.x.x.3 | Azure DNS mapping | +| x.x.x.255 (or last) | Broadcast | + +So a /24 subnet (256 addresses) has **251 usable IPs**, not 256. + +### RFC 1918 private address ranges + +| Range | CIDR | Total IPs | Typical Use | +|-------|------|-----------|-------------| +| 10.0.0.0 – 10.255.255.255 | 10.0.0.0/8 | 16,777,216 | Large enterprise, Azure VNets | +| 172.16.0.0 – 172.31.255.255 | 172.16.0.0/12 | 1,048,576 | Medium enterprise | +| 192.168.0.0 – 192.168.255.255 | 192.168.0.0/16 | 65,536 | Small networks, home labs | + +**Best practice:** Use `10.0.0.0/8` for Azure. It provides the most room for growth and is easiest to summarize. + +## Step 1 — Map Your Existing Address Space + +Before planning Azure IPs, document all existing address usage: + +``` +On-premises networks: + - Datacenter 1: 10.1.0.0/16 + - Datacenter 2: 10.2.0.0/16 + - Branch offices: 10.10.0.0/16 + +Existing Azure VNets: + - Production hub: 10.100.0.0/22 + - Dev/test: 10.200.0.0/22 + +Other clouds (AWS/GCP): + - AWS VPC: 172.16.0.0/16 +``` + +**Critical rule:** Azure VNet address spaces must NOT overlap with on-premises networks if you're using VPN or ExpressRoute connectivity. Overlapping prefixes cause routing failures. + +## Step 2 — Allocate Address Ranges + +### Regional allocation strategy + +Assign a large block per region and subdivide: + +``` +10.0.0.0/8 (entire Azure allocation) +├── 10.0.0.0/16 — Reserved (don't use .0 range for clarity) +├── 10.1.0.0/16 — East US (primary region) +│ ├── 10.1.0.0/22 — Hub VNet +│ ├── 10.1.4.0/22 — Spoke: App1 +│ ├── 10.1.8.0/22 — Spoke: App2 +│ └── 10.1.12.0/22 — Spoke: App3 +├── 10.2.0.0/16 — West US (secondary region) +│ ├── 10.2.0.0/22 — Hub VNet +│ ├── 10.2.4.0/22 — Spoke: App1-DR +│ └── 10.2.8.0/22 — Spoke: App2-DR +├── 10.10.0.0/16 — On-premises (reserved, don't use in Azure) +├── 10.100.0.0/16 — Dev/Test +└── 10.200.0.0/16 — Sandbox +``` + +### Environment-based allocation + +``` +Production: 10.1.0.0/16 – 10.9.0.0/16 +Staging: 10.50.0.0/16 – 10.59.0.0/16 +Dev/Test: 10.100.0.0/16 – 10.109.0.0/16 +Sandbox: 10.200.0.0/16 – 10.209.0.0/16 +On-premises: 10.10.0.0/16 – 10.19.0.0/16 (reserved) +``` + +## Step 3 — Size VNets and Subnets + +### CIDR sizing reference + +| CIDR | Subnet Mask | Total IPs | Usable in Azure | Typical Use | +|------|-------------|-----------|----------------|-------------| +| /28 | 255.255.255.240 | 16 | 11 | Small dedicated subnets (DNS resolver, point-to-site) | +| /27 | 255.255.255.224 | 32 | 27 | GatewaySubnet, small service subnets | +| /26 | 255.255.255.192 | 64 | 59 | AzureFirewallSubnet, AzureBastionSubnet | +| /25 | 255.255.255.128 | 128 | 123 | Small application tiers | +| /24 | 255.255.255.0 | 256 | 251 | Standard workload subnet | +| /23 | 255.255.254.0 | 512 | 507 | Large workload subnet | +| /22 | 255.255.252.0 | 1,024 | 1,019 | Hub VNet, large spoke VNet | +| /21 | 255.255.248.0 | 2,048 | 2,043 | Large spoke VNet | +| /20 | 255.255.240.0 | 4,096 | 4,091 | AKS cluster VNet | +| /16 | 255.255.0.0 | 65,536 | 65,531 | Regional allocation block | + +### VNet sizing guidelines + +| VNet Type | Recommended Size | Rationale | +|-----------|-----------------|-----------| +| Hub VNet | /22 (1,019 IPs) | Firewall, gateway, bastion, DNS, shared services | +| Small spoke | /24 (251 IPs) | Single-tier app with < 50 VMs | +| Medium spoke | /22 (1,019 IPs) | Multi-tier app with 50-200 resources | +| Large spoke | /20 (4,091 IPs) | AKS, large VMSS, data platforms | +| AKS cluster | /20 or larger | Azure CNI assigns pod IPs from subnet | + +### Subnet sizing for Azure services + +Some Azure services have **minimum subnet size requirements:** + +| Azure Service | Minimum Subnet Size | Recommended Size | Notes | +|---------------|---------------------|-----------------|-------| +| GatewaySubnet | /27 | /27 | Must be named "GatewaySubnet" | +| AzureFirewallSubnet | /26 | /26 | Must be named "AzureFirewallSubnet" | +| AzureBastionSubnet | /26 | /26 | Must be named "AzureBastionSubnet" | +| Application Gateway | /26 | /24 | Needs IPs for instances + private frontends | +| API Management (Premium) | /27 | /27 | Dedicated subnet required | +| Azure SQL MI | /27 | /27 | Dedicated subnet with delegation | +| AKS (Azure CNI) | Varies | /20 per node pool | Each pod gets a subnet IP | +| Private Endpoints | /27 minimum | /24 | Each endpoint uses 1 IP | +| DNS Private Resolver Inbound | /28 | /28 | Dedicated, no other resources | +| DNS Private Resolver Outbound | /28 | /28 | Dedicated, no other resources | + +## Step 4 — Plan for Growth + +### The 5x rule + +Plan for **5x your current needs.** If you need 50 IPs today, allocate a subnet that can handle 250. + +Why: +- Adding VMs during scale-out events +- Blue-green deployments temporarily double resource count +- Migration periods run old and new side by side +- Services like AKS consume IPs rapidly (pod IPs) + +### You cannot resize a VNet without downtime + +You CAN add additional address prefixes to a VNet: + +```bash +# Add a second address prefix to an existing VNet +az network vnet update \ + -g \ + -n \ + --address-prefixes 10.1.0.0/22 10.1.4.0/22 +``` + +But you CANNOT shrink or change an existing prefix if subnets are deployed in it. Plan generously upfront. + +## IPv6 Considerations + +Azure supports dual-stack (IPv4 + IPv6) VNets. Key points: + +- IPv6 subnets must be at least /64 +- Most Azure services support dual-stack, but some (like Azure Firewall Basic) do not +- NSGs, UDRs, and peering all work with IPv6 +- Private endpoints are IPv4-only as of 2024 + +```bash +# Add IPv6 address space to existing VNet +az network vnet update \ + -g \ + -n \ + --address-prefixes 10.1.0.0/22 fd00:db8:1::/48 + +# Create dual-stack subnet +az network vnet subnet create \ + -g \ + --vnet-name \ + -n dual-stack-subnet \ + --address-prefixes 10.1.1.0/24 fd00:db8:1:1::/64 +``` + +## Avoiding IP Conflicts + +### Conflict detection checklist + +Before deploying a new VNet: + +1. Check on-premises IPAM (IP Address Management) system +2. List all existing VNet address spaces: + ```bash + az network vnet list --query "[].{name:name, rg:resourceGroup, prefixes:addressSpace.addressPrefixes}" -o table + ``` +3. Check VPN/ExpressRoute local network gateways (advertised on-premises ranges): + ```bash + az network local-gateway list -g --query "[].{name:name, prefixes:localNetworkAddressSpace.addressPrefixes}" + ``` +4. Check for any peering address space overlaps + +### When you do have an overlap + +If you inherit overlapping address spaces (e.g., after a merger): +- **NAT Gateway** can translate between overlapping ranges +- **Azure Firewall DNAT** rules can map overlapping addresses +- **Re-IP one side** — the cleanest solution but most disruptive +- **Private Link** — access PaaS services without worrying about IP overlaps + +## Example: Complete IP Plan for a 3-tier Application + +``` +Region: East US +VNet: app-prod-eastus (10.1.4.0/22) + +Subnets: +├── web-subnet 10.1.4.0/26 (59 usable) — Web tier VMs, App Gateway +├── app-subnet 10.1.4.64/26 (59 usable) — App tier VMs +├── data-subnet 10.1.4.128/26 (59 usable) — Database VMs +├── pe-subnet 10.1.4.192/27 (27 usable) — Private endpoints +├── appgw-subnet 10.1.5.0/24 (251 usable) — Application Gateway (dedicated) +└── reserved 10.1.6.0/23 (507 usable) — Future expansion +``` + +## IP Planning Checklist + +- [ ] Existing on-premises and cloud IP spaces documented +- [ ] Regional allocation blocks assigned (no overlaps) +- [ ] Hub VNet sized for all shared service subnets +- [ ] Spoke VNets sized for current needs + 5x growth +- [ ] Special Azure subnet sizes respected (Gateway, Firewall, Bastion) +- [ ] AKS CIDR requirements calculated (if using Azure CNI) +- [ ] IPv6 requirements assessed +- [ ] Overlap check completed against all environments +- [ ] IP plan documented and added to IPAM system + +## Related Resources + +- [Plan virtual networks](https://learn.microsoft.com/azure/virtual-network/virtual-network-vnet-plan-design-arm) +- [Azure VNet FAQ — address space](https://learn.microsoft.com/azure/virtual-network/virtual-networks-faq#what-address-ranges-can-i-use-in-my-vnets) +- [IPv6 for Azure VNet](https://learn.microsoft.com/azure/virtual-network/ip-services/ipv6-overview) +- For hub-spoke topology → see [hub-spoke-design.md](hub-spoke-design.md) +- For VNet configuration → use `azure-virtual-network` skill diff --git a/plugin/skills/azure-network-architecture/references/landing-zone-networking.md b/plugin/skills/azure-network-architecture/references/landing-zone-networking.md new file mode 100644 index 000000000..ebb7693c7 --- /dev/null +++ b/plugin/skills/azure-network-architecture/references/landing-zone-networking.md @@ -0,0 +1,301 @@ +# Azure Landing Zone Networking + +Network topology and design guidance for Azure landing zones following the Cloud Adoption Framework (CAF) enterprise-scale architecture. + +## Landing Zone Network Architecture Overview + +The Azure landing zone architecture separates networking into a **connectivity subscription** that hosts shared networking resources, with application **landing zone subscriptions** hosting workloads in spoke VNets. + +``` +Management Group Hierarchy +├── Platform +│ ├── Connectivity (subscription) +│ │ ├── Hub VNet or Virtual WAN +│ │ ├── VPN/ExpressRoute Gateways +│ │ ├── Azure Firewall +│ │ ├── DNS (Private Resolver, Private DNS Zones) +│ │ └── Azure Bastion +│ ├── Identity (subscription) +│ │ └── AD DS / Entra DS VMs +│ └── Management (subscription) +│ └── Log Analytics, Automation +└── Landing Zones + ├── Corp (connected to hub) + │ ├── App1-subscription → Spoke VNet → Peered to Hub + │ └── App2-subscription → Spoke VNet → Peered to Hub + └── Online (internet-facing) + └── WebApp-subscription → Spoke VNet → Peered to Hub +``` + +## Connectivity Subscription Design + +The connectivity subscription is the networking backbone. All shared networking resources live here. + +### Option A: Hub VNet with VNet Peering + +Best for organizations with < 30 spokes and networking expertise. + +```bash +# Create connectivity subscription resources +az network vnet create \ + -g connectivity-rg \ + -n hub-vnet \ + --address-prefix 10.0.0.0/22 \ + --location + +# Gateway subnet for VPN/ExpressRoute +az network vnet subnet create \ + -g connectivity-rg \ + --vnet-name hub-vnet \ + -n GatewaySubnet \ + --address-prefix 10.0.0.0/27 + +# Firewall subnet +az network vnet subnet create \ + -g connectivity-rg \ + --vnet-name hub-vnet \ + -n AzureFirewallSubnet \ + --address-prefix 10.0.0.64/26 + +# Bastion subnet +az network vnet subnet create \ + -g connectivity-rg \ + --vnet-name hub-vnet \ + -n AzureBastionSubnet \ + --address-prefix 10.0.0.128/26 +``` + +### Option B: Azure Virtual WAN + +Best for organizations with 30+ spokes, multiple branches, or wanting managed routing. + +```bash +# Create Virtual WAN +az network vwan create \ + -g connectivity-rg \ + -n enterprise-vwan \ + --type Standard \ + --location + +# Create Virtual WAN Hub +az network vhub create \ + -g connectivity-rg \ + -n hub- \ + --vwan enterprise-vwan \ + --address-prefix 10.0.0.0/23 \ + --location +``` + +### Decision: Hub VNet vs Virtual WAN + +| Factor | Hub VNet | Virtual WAN | +|--------|----------|-------------| +| Number of spokes | < 30 | 30+ | +| Branch offices with SD-WAN | Not integrated | Integrated partner support | +| Routing complexity | Manual UDRs | Managed by Virtual WAN | +| NVA flexibility | Full control | Limited to integrated partners | +| Cost predictability | Pay per resource | Hub fee + routing charges | +| Migration from existing | Easier (familiar patterns) | Requires architectural change | + +## Hub VNet Services + +### Azure Firewall + +Central inspection and filtering point for all traffic: + +```bash +# Create Azure Firewall in hub +az network firewall create \ + -g connectivity-rg \ + -n hub-firewall \ + --location \ + --sku AZFW_VNet \ + --tier Premium \ + --vnet-name hub-vnet + +# Create firewall policy (centrally managed) +az network firewall policy create \ + -g connectivity-rg \ + -n enterprise-fw-policy \ + --sku Premium \ + --threat-intel-mode Deny +``` + +### VPN/ExpressRoute Gateway + +```bash +# Create ExpressRoute Gateway (if using ExpressRoute) +az network vnet-gateway create \ + -g connectivity-rg \ + -n er-gateway \ + --vnet hub-vnet \ + --gateway-type ExpressRoute \ + --sku ErGw2AZ \ + --location + +# Create VPN Gateway (if using VPN, or as backup for ER) +az network vnet-gateway create \ + -g connectivity-rg \ + -n vpn-gateway \ + --vnet hub-vnet \ + --gateway-type Vpn \ + --vpn-type RouteBased \ + --sku VpnGw2AZ \ + --location +``` + +### DNS Architecture + +Centralize DNS in the connectivity subscription: + +```bash +# Create DNS Private Resolver in hub +az dns-resolver create \ + -g connectivity-rg \ + -n hub-dns-resolver \ + --location \ + --id /subscriptions//resourceGroups/connectivity-rg/providers/Microsoft.Network/virtualNetworks/hub-vnet + +# Create Private DNS zones for Azure services (link to hub VNet) +for zone in privatelink.blob.core.windows.net privatelink.database.windows.net privatelink.vaultcore.azure.net; do + az network private-dns zone create -g connectivity-rg -n $zone + az network private-dns link vnet create \ + -g connectivity-rg \ + -z $zone \ + -n hub-link \ + --virtual-network hub-vnet \ + --registration-enabled false +done +``` + +**Critical DNS design decisions:** +- Private DNS zones should be in the **connectivity subscription** (not in each workload subscription) +- All VNets (hub + spokes) must be linked to the Private DNS zones +- On-premises DNS servers forward Azure zones to the DNS Private Resolver inbound endpoint +- Use DNS forwarding rulesets for Azure-to-on-premises resolution + +## Application Landing Zone Networking + +Each application team gets a subscription with a spoke VNet peered to the hub. + +### Spoke VNet template + +```bash +# Create spoke VNet in the application subscription +az network vnet create \ + -g \ + -n spoke- \ + --address-prefix 10.1.0.0/24 \ + --location + +# Create workload subnets +az network vnet subnet create \ + -g \ + --vnet-name spoke- \ + -n web-subnet \ + --address-prefix 10.1.0.0/26 + +az network vnet subnet create \ + -g \ + --vnet-name spoke- \ + -n app-subnet \ + --address-prefix 10.1.0.64/26 + +az network vnet subnet create \ + -g \ + --vnet-name spoke- \ + -n data-subnet \ + --address-prefix 10.1.0.128/26 + +# Peer to hub (cross-subscription peering) +az network vnet peering create \ + -g \ + --name spoke-to-hub \ + --vnet-name spoke- \ + --remote-vnet /subscriptions//resourceGroups/connectivity-rg/providers/Microsoft.Network/virtualNetworks/hub-vnet \ + --allow-vnet-access \ + --allow-forwarded-traffic \ + --use-remote-gateways +``` + +### Apply governance: route all traffic through firewall + +```bash +# Create route table for the spoke +az network route-table create -g -n spoke-rt --location + +# Default route through hub firewall +az network route-table route create \ + -g \ + --route-table-name spoke-rt \ + -n to-firewall \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address + +# Associate with all workload subnets +az network vnet subnet update -g --vnet-name spoke- -n web-subnet --route-table spoke-rt +az network vnet subnet update -g --vnet-name spoke- -n app-subnet --route-table spoke-rt +az network vnet subnet update -g --vnet-name spoke- -n data-subnet --route-table spoke-rt +``` + +### Private DNS zone links for spoke VNets + +```bash +# Link spoke VNet to all Private DNS zones in connectivity subscription +for zone in privatelink.blob.core.windows.net privatelink.database.windows.net privatelink.vaultcore.azure.net; do + az network private-dns link vnet create \ + -g connectivity-rg \ + -z $zone \ + -n spoke--link \ + --virtual-network /subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks/spoke- \ + --registration-enabled false +done +``` + +## Multi-Region Landing Zone + +For disaster recovery and global presence, deploy hub infrastructure in multiple regions: + +- **Primary region:** Full hub (Firewall + Gateway + DNS + Bastion) +- **Secondary region:** Full hub (Firewall + Gateway + DNS + Bastion) +- **Hub-to-hub:** Global VNet peering or ExpressRoute Global Reach +- **DNS:** Consistent across regions (same Private DNS zones linked to both hubs) +- **Firewall Policy:** Use Azure Firewall Manager for central policy across regions + +## Platform vs Application Landing Zone Responsibilities + +| Responsibility | Platform Team | Application Team | +|---------------|:-------------:|:----------------:| +| Hub VNet design | ✅ | | +| Firewall rules (network level) | ✅ | | +| VPN/ExpressRoute configuration | ✅ | | +| DNS zone management | ✅ | | +| Spoke VNet creation | ✅ (or delegated) | | +| NSG rules within spoke | | ✅ | +| Application Gateway in spoke | | ✅ | +| Private endpoints | | ✅ | +| Workload deployment | | ✅ | + +## Landing Zone Networking Checklist + +- [ ] Connectivity subscription created with hub VNet or Virtual WAN +- [ ] Azure Firewall deployed with baseline policy (deny all, allow known traffic) +- [ ] VPN and/or ExpressRoute gateway deployed and connected +- [ ] DNS Private Resolver deployed with forwarding rules +- [ ] Private DNS zones created for all required Azure services +- [ ] Azure Bastion deployed in hub for management access +- [ ] IP address plan finalized — no overlaps between hub, spokes, and on-premises +- [ ] Azure Policy to enforce NSGs on all subnets +- [ ] Azure Policy to enforce UDRs directing traffic through firewall +- [ ] Network Watcher enabled in all regions +- [ ] NSG flow logs enabled and sent to Log Analytics + +## Related Resources + +- [CAF enterprise-scale landing zone](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/landing-zone/) +- [Define an Azure network topology](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/define-an-azure-network-topology) +- [Hub-spoke topology](https://learn.microsoft.com/azure/architecture/networking/architecture/hub-spoke) +- For hub-spoke design details → see [hub-spoke-design.md](hub-spoke-design.md) +- For IP planning → see [ip-planning.md](ip-planning.md) +- For segmentation → see [segmentation.md](segmentation.md) diff --git a/plugin/skills/azure-network-architecture/references/lb-selection-guide.md b/plugin/skills/azure-network-architecture/references/lb-selection-guide.md new file mode 100644 index 000000000..0f191cf10 --- /dev/null +++ b/plugin/skills/azure-network-architecture/references/lb-selection-guide.md @@ -0,0 +1,231 @@ +# Load Balancer Selection Guide + +Decision tree for choosing between Azure Load Balancer, Application Gateway, Front Door, and Traffic Manager based on workload requirements. + +## The Decision Tree + +Start with these three questions: + +### Question 1: HTTP/HTTPS or non-HTTP? + +| Answer | Next Step | +|--------|-----------| +| **HTTP/HTTPS** (web apps, APIs, REST) | Go to Question 2 | +| **Non-HTTP** (TCP, UDP, databases, gaming, IoT) | → **Azure Load Balancer** | + +### Question 2: Global or regional? + +| Answer | Next Step | +|--------|-----------| +| **Global** (multi-region, global users) | Go to Question 3 | +| **Regional** (single region, or region-specific) | → **Application Gateway** | + +### Question 3: Need CDN, edge WAF, or SSL offload at edge? + +| Answer | Recommendation | +|--------|---------------| +| **Yes** — need CDN, edge caching, global WAF, or low-latency edge termination | → **Azure Front Door** | +| **No** — just DNS-based failover between regions | → **Traffic Manager** | + +## Service Comparison Matrix + +| Feature | Load Balancer | Application Gateway | Front Door | Traffic Manager | +|---------|:------------:|:-------------------:|:----------:|:--------------:| +| **Layer** | L4 (TCP/UDP) | L7 (HTTP/HTTPS) | L7 (HTTP/HTTPS) | DNS-based | +| **Scope** | Regional | Regional | Global | Global | +| **SSL termination** | No | Yes | Yes (edge) | No | +| **URL-based routing** | No | Yes | Yes | No | +| **WAF** | No | Yes (v2) | Yes (edge) | No | +| **Session affinity** | Hash-based | Cookie-based | Cookie-based | No | +| **Health probes** | TCP/HTTP | HTTP/HTTPS | HTTP/HTTPS | HTTP/HTTPS/TCP | +| **WebSocket** | Yes (pass-through) | Yes | Yes | N/A | +| **Private only** | Yes | Yes | No (internet-facing) | No | +| **Cross-region** | Yes (cross-region LB) | No | Yes | Yes (DNS) | +| **Autoscaling** | N/A | Yes (v2) | Yes | N/A | +| **Pricing model** | Rules + data | Capacity units | Base + routing + data | Queries + health checks | + +## When to Use Each Service + +### Azure Load Balancer + +**Use for:** +- Non-HTTP traffic (SQL, RDP, SSH, custom TCP/UDP protocols) +- Internal load balancing between application tiers +- HA ports for NVA deployments +- Ultra-low latency requirements (no L7 processing overhead) +- UDP workloads (gaming, VoIP, DNS) + +**SKUs:** +- **Standard** — production use, zone-redundant, any backend pool size +- **Gateway** — chaining third-party NVAs transparently +- **Cross-region** — global L4 load balancing with regional LB backends + +```bash +# Create a Standard internal load balancer +az network lb create \ + --resource-group \ + --name my-ilb \ + --sku Standard \ + --vnet-name \ + --subnet \ + --frontend-ip-name frontend \ + --backend-pool-name backend-pool +``` + +### Application Gateway + +**Use for:** +- Regional HTTP/HTTPS load balancing +- SSL termination with centralized certificate management +- URL-based routing (e.g., `/api/*` to one pool, `/images/*` to another) +- Web Application Firewall (WAF v2) for OWASP protection +- Internal web application load balancing (not internet-facing) +- Mutual TLS (mTLS) authentication +- WebSocket and HTTP/2 support + +**Choose v2 SKU** — v1 is legacy. v2 supports autoscaling, zone redundancy, and better performance. + +```bash +# Create Application Gateway v2 with WAF +az network application-gateway create \ + --resource-group \ + --name my-appgw \ + --sku WAF_v2 \ + --capacity 2 \ + --vnet-name \ + --subnet appgw-subnet \ + --public-ip-address appgw-pip \ + --http-settings-port 80 \ + --http-settings-protocol Http +``` + +### Azure Front Door + +**Use for:** +- Global HTTP/HTTPS load balancing with anycast +- CDN and edge caching for static content +- Edge-based WAF (DDoS and bot protection at the edge) +- SSL offload at global edge POPs (reduces latency for TLS handshake) +- Multi-region active-active web applications +- A/B testing and weighted routing +- Instant global failover (< 30 seconds) + +**Tiers:** +- **Standard** — CDN + basic routing +- **Premium** — CDN + WAF + Private Link origins + advanced analytics + +```bash +# Create Front Door profile +az afd profile create \ + --resource-group \ + --profile-name my-frontdoor \ + --sku Premium_AzureFrontDoor +``` + +**Front Door + Application Gateway combo:** Use Front Door for global distribution and WAF at the edge, with Application Gateway as the regional origin for URL routing and additional WAF rules. + +### Traffic Manager + +**Use for:** +- DNS-based global traffic routing (non-HTTP workloads that need multi-region) +- Simple failover between regions (primary/secondary) +- Geographic routing (route users to nearest region by DNS) +- Weighted round-robin between endpoints +- Nested profiles for complex routing hierarchies + +**Limitations:** +- DNS-based only — no inline processing of traffic +- Failover speed depends on DNS TTL (typically 30-60 seconds) +- No SSL termination, no WAF, no caching +- Clients may cache DNS and not respect TTL changes + +```bash +# Create Traffic Manager profile with priority routing +az network traffic-manager profile create \ + --resource-group \ + --name my-tm \ + --routing-method Priority \ + --unique-dns-name my-app-tm \ + --monitor-protocol HTTPS \ + --monitor-port 443 \ + --monitor-path "/health" +``` + +## Common Combination Patterns + +### Pattern 1: Global web app (most common) + +``` +Users → Front Door (global L7 + WAF + CDN) + → Application Gateway (regional L7 + URL routing) + → VMs / VMSS / App Service +``` + +### Pattern 2: Multi-tier application + +``` +Internet → Application Gateway (L7, SSL, WAF) + → Web tier VMs + → Internal Load Balancer (L4) + → App tier VMs + → Internal Load Balancer (L4) + → Database tier +``` + +### Pattern 3: Global non-HTTP service + +``` +Users → Traffic Manager (DNS routing) + → Azure Load Balancer (regional L4) + → Backend VMs (TCP/UDP service) +``` + +### Pattern 4: NVA high availability + +``` +Spoke traffic → UDR → + Gateway Load Balancer (transparent chaining) + → NVA instance 1 + → NVA instance 2 + → Azure Load Balancer (destination) + → Backend VMs +``` + +## Quick Decision Cheat Sheet + +| Scenario | Service | +|----------|---------| +| Internal TCP load balancing | **Load Balancer** (Standard, internal) | +| Public-facing web app, single region | **Application Gateway** (v2 + WAF) | +| Public-facing web app, global users | **Front Door** (Premium) | +| Global failover for any protocol | **Traffic Manager** | +| NVA transparent chaining | **Gateway Load Balancer** | +| L4 global distribution | **Cross-region Load Balancer** | +| API with URL-based routing | **Application Gateway** or **Front Door** | +| Static site with CDN | **Front Door** (Standard) | +| Gaming / UDP workload | **Load Balancer** (Standard) | +| Micro-services with multiple backends | **Application Gateway** (URL routing) | + +## Pricing Comparison (approximate) + +| Service | Base Cost | Data Processing | +|---------|-----------|----------------| +| Load Balancer (Standard) | ~$18/month per rule | ~$5/TB | +| Application Gateway (v2) | ~$175/month (2 instances) | ~$8/CU (capacity units) | +| Front Door (Standard) | ~$35/month base | ~$0.01-0.02/GB routing | +| Front Door (Premium) | ~$330/month base | ~$0.02-0.03/GB routing | +| Traffic Manager | ~$0.75/million queries | Health check: $0.36/endpoint/month | + +Prices are approximate and region-dependent. Check [Azure pricing calculator](https://azure.microsoft.com/pricing/calculator/) for current rates. + +## Related Resources + +- [Choose a load balancing solution — Microsoft decision tree](https://learn.microsoft.com/azure/architecture/guide/technology-choices/load-balancing-overview) +- [Azure Load Balancer overview](https://learn.microsoft.com/azure/load-balancer/load-balancer-overview) +- [Application Gateway overview](https://learn.microsoft.com/azure/application-gateway/overview) +- [Azure Front Door overview](https://learn.microsoft.com/azure/frontdoor/front-door-overview) +- [Traffic Manager overview](https://learn.microsoft.com/azure/traffic-manager/traffic-manager-overview) +- For Load Balancer configuration → use `azure-load-balancer` skill +- For Application Gateway configuration → use `azure-application-gateway` skill +- For Front Door configuration → use `azure-front-door` skill +- For Traffic Manager configuration → use `azure-traffic-manager` skill diff --git a/plugin/skills/azure-network-architecture/references/segmentation.md b/plugin/skills/azure-network-architecture/references/segmentation.md new file mode 100644 index 000000000..48295988d --- /dev/null +++ b/plugin/skills/azure-network-architecture/references/segmentation.md @@ -0,0 +1,300 @@ +# Network Segmentation Strategies + +Design network segmentation for Azure workloads using subnets, NSGs, ASGs, Azure Firewall, and zero-trust patterns. + +## Why Segmentation Matters + +Network segmentation limits blast radius. If an attacker compromises a web server, segmentation prevents lateral movement to database servers. Azure provides multiple segmentation layers: + +``` +Layer 1: Subscription/VNet boundaries (strongest isolation) +Layer 2: Subnet + NSG (standard segmentation) +Layer 3: ASGs within subnets (micro-segmentation) +Layer 4: Azure Firewall between segments (inspection + logging) +Layer 5: Private endpoints (PaaS isolation) +``` + +## Segmentation Approaches + +### Approach 1: Subnet-Based Segmentation (Recommended Starting Point) + +Separate workload tiers into different subnets with NSGs controlling traffic between them. + +``` +VNet: 10.1.0.0/22 +├── web-subnet 10.1.0.0/26 NSG: allow 443 from Internet +├── app-subnet 10.1.0.64/26 NSG: allow 8080 from web-subnet only +├── data-subnet 10.1.0.128/26 NSG: allow 1433 from app-subnet only +└── mgmt-subnet 10.1.0.192/26 NSG: allow 22/3389 from Bastion only +``` + +```bash +# Create NSG for app subnet — only allow traffic from web subnet +az network nsg create -g -n app-nsg + +az network nsg rule create \ + -g --nsg-name app-nsg \ + -n AllowFromWeb \ + --priority 100 \ + --direction Inbound \ + --access Allow \ + --protocol Tcp \ + --source-address-prefixes 10.1.0.0/26 \ + --destination-port-ranges 8080 + +az network nsg rule create \ + -g --nsg-name app-nsg \ + -n DenyAllInbound \ + --priority 4000 \ + --direction Inbound \ + --access Deny \ + --protocol '*' \ + --source-address-prefixes '*' \ + --destination-port-ranges '*' + +# Associate NSG with subnet +az network vnet subnet update \ + -g --vnet-name -n app-subnet \ + --network-security-group app-nsg +``` + +### Approach 2: Application Security Groups (Micro-Segmentation) + +ASGs let you group VMs by role and write NSG rules that reference groups instead of IP addresses. This is ideal when VMs in the same subnet have different roles. + +```bash +# Create ASGs for each application role +az network asg create -g -n web-servers +az network asg create -g -n app-servers +az network asg create -g -n db-servers + +# Associate VM NICs with ASGs +az network nic ip-config update \ + -g --nic-name web-vm-nic \ + -n ipconfig1 \ + --application-security-groups web-servers + +az network nic ip-config update \ + -g --nic-name app-vm-nic \ + -n ipconfig1 \ + --application-security-groups app-servers + +# Create NSG rules using ASGs +az network nsg rule create \ + -g --nsg-name workload-nsg \ + -n WebToApp \ + --priority 100 \ + --direction Inbound \ + --access Allow \ + --protocol Tcp \ + --source-asgs web-servers \ + --destination-asgs app-servers \ + --destination-port-ranges 8080 + +az network nsg rule create \ + -g --nsg-name workload-nsg \ + -n AppToDb \ + --priority 110 \ + --direction Inbound \ + --access Allow \ + --protocol Tcp \ + --source-asgs app-servers \ + --destination-asgs db-servers \ + --destination-port-ranges 1433 +``` + +**ASG advantages:** +- Rules follow the VM, not the IP — works with dynamic IP assignment +- A VM can belong to multiple ASGs +- Cleaner rule sets than managing CIDR ranges + +### Approach 3: Azure Firewall Between Segments + +For environments requiring deep inspection, logging, and threat intelligence between segments, route inter-subnet traffic through Azure Firewall. + +```bash +# Create UDR to route web→app traffic through firewall +az network route-table create -g -n web-rt +az network route-table route create \ + -g --route-table-name web-rt \ + -n to-app-via-fw \ + --address-prefix 10.1.0.64/26 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address + +# Create UDR for return traffic +az network route-table create -g -n app-rt +az network route-table route create \ + -g --route-table-name app-rt \ + -n to-web-via-fw \ + --address-prefix 10.1.0.0/26 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address + +# Associate route tables +az network vnet subnet update -g --vnet-name -n web-subnet --route-table web-rt +az network vnet subnet update -g --vnet-name -n app-subnet --route-table app-rt +``` + +Then create firewall rules to allow specific traffic: +```bash +# Application rule collection for web-to-app +az network firewall network-rule create \ + -g -f \ + --collection-name web-to-app \ + --priority 200 \ + --action Allow \ + -n allow-app-traffic \ + --protocols TCP \ + --source-addresses 10.1.0.0/26 \ + --destination-addresses 10.1.0.64/26 \ + --destination-ports 8080 +``` + +### Approach 4: VNet Manager Security Admin Rules + +Azure Virtual Network Manager provides centralized security rules that platform teams can enforce across multiple VNets. These rules have higher priority than NSGs and cannot be overridden by workload teams. + +```bash +# Create security admin configuration +az network manager security-admin-config create \ + -g \ + --network-manager-name \ + -n baseline-security + +# Create a rule collection +az network manager security-admin-config rule-collection create \ + -g \ + --network-manager-name \ + --configuration-name baseline-security \ + -n deny-risky-ports \ + --applies-to-groups "[{networkGroupId:}]" + +# Add a rule to deny SSH from Internet (enforced by platform) +az network manager security-admin-config rule-collection rule create \ + -g \ + --network-manager-name \ + --configuration-name baseline-security \ + --rule-collection-name deny-risky-ports \ + -n deny-ssh-from-internet \ + --kind Custom \ + --protocol Tcp \ + --direction Inbound \ + --access Deny \ + --priority 100 \ + --source-address-prefixes "Internet" \ + --dest-port-ranges 22 +``` + +## Zero Trust Networking in Azure + +Zero trust means "never trust, always verify" — even for traffic within the network. + +### Zero Trust Principles for Azure Networking + +1. **Verify explicitly:** Authenticate and authorize every network flow +2. **Least privilege access:** Only allow the minimum required traffic +3. **Assume breach:** Segment aggressively, encrypt in transit, log everything + +### Implementing Zero Trust + +``` +┌─────────────────────────────────────────────┐ +│ Internet │ +└──────────┬──────────────────────┬────────────┘ + │ │ + ┌──────▼──────┐ ┌──────▼──────┐ + │ Front Door │ │ DDoS Prot. │ + │ (WAF) │ │ │ + └──────┬──────┘ └─────────────┘ + │ + ┌──────▼──────┐ + │ App Gateway │ ← SSL termination, mTLS + │ (WAF v2) │ + └──────┬──────┘ + │ + ┌──────▼──────┐ + │ NSG + ASG │ ← Micro-segmentation + │ Web Tier │ + └──────┬──────┘ + │ ← Azure Firewall inspection + ┌──────▼──────┐ + │ NSG + ASG │ + │ App Tier │ + └──────┬──────┘ + │ ← Private Endpoint + ┌──────▼──────┐ + │ Azure SQL │ ← Public endpoint disabled + │ (PaaS) │ + └─────────────┘ +``` + +### Zero Trust checklist + +- [ ] **Disable public endpoints** on all PaaS services — use Private Endpoints +- [ ] **NSGs on every subnet** with explicit allow rules (default deny) +- [ ] **Azure Firewall** for east-west traffic inspection between tiers +- [ ] **WAF** on all internet-facing HTTP endpoints (Front Door or App Gateway) +- [ ] **DDoS Protection** Standard on the VNet +- [ ] **Encryption in transit** — TLS 1.2+ everywhere, no unencrypted traffic +- [ ] **NSG flow logs** enabled for all NSGs → sent to Log Analytics +- [ ] **Azure Firewall logs** sent to Log Analytics +- [ ] **Just-in-time VM access** via Microsoft Defender for Cloud +- [ ] **No public IPs on VMs** — use Azure Bastion for management access +- [ ] **Service endpoints or Private Link** for Azure-to-Azure PaaS traffic +- [ ] **DNS** — use Private DNS zones so names resolve to private IPs + +## Segmentation Patterns by Workload + +### Three-tier web application + +``` +NSG rules: + web-subnet ← allow 443 from Internet, 80 from AppGw subnet + app-subnet ← allow 8080 from web-subnet only + data-subnet ← allow 1433 from app-subnet only + all subnets ← deny all other inbound +``` + +### Microservices (AKS) + +``` +AKS network policy (Calico or Azure): + - Namespace-level isolation (each microservice in its own namespace) + - Allow: frontend → backend-api (port 8080) + - Allow: backend-api → database (port 5432) + - Deny: frontend → database (no direct access) + - Allow: all → monitoring namespace (metrics export) +``` + +### Shared services hub + +``` +Hub segmentation: + AzureFirewallSubnet ← managed by Azure, no NSG needed + GatewaySubnet ← managed by Azure, no NSG needed + AzureBastionSubnet ← managed by Azure (auto NSG rules) + shared-services ← NSG: allow RDP/SSH from Bastion subnet only + dns-resolver-inbound ← NSG: allow DNS (53) from all VNets +``` + +## Comparing Segmentation Tools + +| Tool | Scope | Managed By | Overridable | Best For | +|------|-------|-----------|-------------|----------| +| NSG | Subnet or NIC | Workload team | Yes | Standard per-workload segmentation | +| ASG | VM group within NSG | Workload team | Yes | Role-based micro-segmentation | +| Azure Firewall | VNet or cross-VNet | Platform team | No (by workload) | Central inspection, logging, threat intel | +| VNet Manager Admin Rules | Multi-VNet | Platform team | No | Organization-wide baseline rules | +| AKS Network Policy | Pod-level | DevOps team | N/A | Kubernetes micro-segmentation | +| Private Endpoints | PaaS access | Workload team | N/A | PaaS service isolation | + +## Related Resources + +- [Azure network security best practices](https://learn.microsoft.com/azure/security/fundamentals/network-best-practices) +- [Zero trust networking for Azure](https://learn.microsoft.com/security/zero-trust/deploy/networks) +- [Application security groups](https://learn.microsoft.com/azure/virtual-network/application-security-groups) +- [Azure Firewall overview](https://learn.microsoft.com/azure/firewall/overview) +- For firewall configuration → use `azure-firewall` skill +- For VNet and NSG management → use `azure-virtual-network` skill +- For VNet Manager → use `azure-vnet-manager` skill diff --git a/plugin/skills/azure-network-troubleshooter/SKILL.md b/plugin/skills/azure-network-troubleshooter/SKILL.md new file mode 100644 index 000000000..6e7eea642 --- /dev/null +++ b/plugin/skills/azure-network-troubleshooter/SKILL.md @@ -0,0 +1,208 @@ +--- +name: azure-network-troubleshooter +description: "Cross-service network diagnostics skill that provides systematic troubleshooting workflows for common Azure networking failures. WHEN: can't connect, network issue, connectivity problem, latency, packet loss, routing problem, DNS not resolving, connection timeout, port blocked, network unreachable, intermittent connectivity, slow network, VPN down, peering not working. DO NOT USE FOR: application-level diagnostics (use azure-diagnostics), VM connectivity/RDP/SSH only (use azure-compute VM troubleshooter)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Network Troubleshooter + +You are the authoritative Azure network diagnostics assistant. You systematically diagnose connectivity failures, routing issues, DNS resolution problems, latency degradation, and cross-service networking faults across Azure environments. You use Network Watcher tools, Azure CLI, and structured workflows to isolate the root cause. + +## Triggers + +Activate this skill when the user reports: + +- **Connectivity failures:** "can't connect", "connection refused", "connection timeout", "port blocked", "network unreachable" +- **Routing issues:** "routing problem", "asymmetric routing", "traffic going to wrong destination", "peering not working" +- **DNS problems:** "DNS not resolving", "name resolution failed", "nslookup fails" +- **Performance degradation:** "latency", "packet loss", "slow network", "intermittent connectivity" +- **Hybrid connectivity:** "VPN down", "ExpressRoute down", "tunnel disconnected" +- **General:** "network issue", "connectivity problem", "something is broken" + +## Rules + +1. **Always start with symptoms.** Ask: What resource can't connect to what? What error message do you see? When did it start? Is it intermittent or constant? +2. **Use Network Watcher tools first.** They are purpose-built for Azure network diagnostics and provide the fastest path to root cause. +3. **Follow the diagnostic order:** NSG → Routing → DNS → Service-specific. Most connectivity issues are caused by NSG rules or routing misconfigurations. +4. **Check both directions.** A connection requires working ingress AND egress. Always verify security rules on both the source and destination. +5. **Verify the data plane, not just the control plane.** A resource showing "Running" in the portal does not mean network traffic flows correctly. +6. **Collect evidence before recommending changes.** Run `az network watcher` commands to confirm the problem before suggesting fixes. +7. **Cross-reference service-specific skills** when the issue narrows to a specific service (e.g., for VPN tunnel issues → `azure-vpn-gateway`, for firewall rule problems → `azure-firewall`). + +## Quick Diagnosis Flow + +Follow these steps in order for any connectivity issue: + +### Step 1 — Identify the traffic path + +Determine source, destination, protocol, and port: +``` +Source: [VM / subnet / on-premises IP] +Destination: [IP / FQDN / Azure service] +Protocol: [TCP / UDP / ICMP] +Port: [destination port number] +Direction: [inbound / outbound / east-west] +``` + +### Step 2 — Check NSG rules (most common cause) + +```bash +# Verify if traffic is allowed by NSGs +az network watcher test-ip-flow \ + --resource-group \ + --vm \ + --direction \ + --protocol \ + --local \ + --remote + +# View effective security rules on the NIC +az network nic list-effective-nsg \ + --resource-group \ + --name +``` + +If the result shows "Access Denied" → see [references/nsg-analysis.md](references/nsg-analysis.md) + +### Step 3 — Check routing + +```bash +# Determine next hop for traffic +az network watcher show-next-hop \ + --resource-group \ + --vm \ + --source-ip \ + --dest-ip + +# View effective routes on the NIC +az network nic show-effective-route-table \ + --resource-group \ + --name +``` + +If next hop is unexpected or "None" → see [references/routing-debug.md](references/routing-debug.md) + +### Step 4 — Check DNS resolution + +```bash +# Test DNS resolution from inside a VM (via run-command) +az vm run-command invoke \ + --resource-group \ + --name \ + --command-id RunShellScript \ + --scripts "nslookup " + +# Check private DNS zone records +az network private-dns record-set list \ + --resource-group \ + --zone-name + +# Check private DNS zone VNet links +az network private-dns link vnet list \ + --resource-group \ + --zone-name +``` + +If DNS fails → see [references/dns-debug.md](references/dns-debug.md) + +### Step 5 — Run end-to-end connectivity test + +```bash +# Connection troubleshoot (tests full path) +az network watcher test-connectivity \ + --resource-group \ + --source-resource \ + --dest-resource \ + --dest-port + +# Or test to an external endpoint +az network watcher test-connectivity \ + --resource-group \ + --source-resource \ + --dest-address \ + --dest-port +``` + +### Step 6 — Check resource health and activity logs + +```bash +# Check resource health +az resource show \ + --ids \ + --query "properties.provisioningState" + +# Review recent activity logs for networking changes +az monitor activity-log list \ + --resource-group \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ + --query "[?contains(operationName.value, 'Microsoft.Network')].{op:operationName.value, status:status.value, time:eventTimestamp}" \ + --output table +``` + +## Routing Table + +Use this table to route to the appropriate diagnostic workflow based on symptoms: + +| Symptom | Primary Check | Reference Doc | Service Skill | +|---------|--------------|---------------|---------------| +| VM can't reach another VM | NSG IP flow verify → routes | [connectivity-checklist.md](references/connectivity-checklist.md) | `azure-virtual-network` | +| VM can't reach the internet | NAT Gateway / public IP / default route | [routing-debug.md](references/routing-debug.md) | `azure-nat-gateway` | +| VM can't reach on-premises | VPN/ER tunnel status → BGP routes | [routing-debug.md](references/routing-debug.md) | `azure-vpn-gateway` / `azure-expressroute` | +| On-premises can't reach Azure VM | NSG inbound rules → VPN/ER routes | [connectivity-checklist.md](references/connectivity-checklist.md) | `azure-vpn-gateway` / `azure-expressroute` | +| DNS name not resolving | Private DNS zone → VNet link → resolver | [dns-debug.md](references/dns-debug.md) | `azure-dns` | +| Traffic denied by firewall | Firewall rules → DNAT → network rules | [nsg-analysis.md](references/nsg-analysis.md) | `azure-firewall` | +| NSG blocking traffic | Effective rules → priority conflicts | [nsg-analysis.md](references/nsg-analysis.md) | `azure-virtual-network` | +| Asymmetric routing | Effective routes → UDR conflicts | [routing-debug.md](references/routing-debug.md) | `azure-virtual-network` | +| VPN tunnel down | Connection status → IKE logs → shared key | [connectivity-checklist.md](references/connectivity-checklist.md) | `azure-vpn-gateway` | +| Peering not working | Peering state → address space → route propagation | [routing-debug.md](references/routing-debug.md) | `azure-virtual-network` | +| High latency | Connection monitor → hop analysis | [latency-debug.md](references/latency-debug.md) | `azure-network-watcher` | +| Intermittent connectivity | Flow logs → connection monitor → resource health | [latency-debug.md](references/latency-debug.md) | `azure-network-watcher` | +| Packet loss | Connection monitor → MTU check → NVA throughput | [latency-debug.md](references/latency-debug.md) | `azure-network-watcher` | +| Load balancer health probe failing | Probe config → NSG on backend → backend health | [connectivity-checklist.md](references/connectivity-checklist.md) | `azure-load-balancer` | +| Private endpoint not reachable | Private DNS zone → A record → NIC IP | [dns-debug.md](references/dns-debug.md) | `azure-private-link` | +| Application Gateway 502 errors | Backend health → NSG → backend pool config | [connectivity-checklist.md](references/connectivity-checklist.md) | `azure-application-gateway` | + +## MCP Tools + +Use these Azure MCP server operations for diagnostics: + +- `azure__network` — Query NSG rules, route tables, VNet peerings, public IPs, and NIC configurations +- `az network watcher test-ip-flow` — Verify NSG allows or denies traffic for a specific flow +- `az network watcher show-next-hop` — Determine the next hop for a given destination +- `az network watcher test-connectivity` — End-to-end connectivity test between resources +- `az network watcher flow-log show` — View NSG flow log configuration +- `az network watcher connection-monitor` — Continuous connectivity monitoring +- `az network watcher packet-capture` — Capture packets on a VM NIC for deep analysis +- `az network nic list-effective-nsg` — View all effective NSG rules on a NIC +- `az network nic show-effective-route-table` — View all effective routes on a NIC + +## CLI Diagnostic Commands Reference + +```bash +# Comprehensive diagnostic one-liner: IP flow + next hop + effective routes +az network watcher test-ip-flow --vm -g --direction Outbound --protocol TCP --local "10.0.1.4:*" --remote "10.0.2.4:443" +az network watcher show-next-hop --vm -g --source-ip 10.0.1.4 --dest-ip 10.0.2.4 +az network nic show-effective-route-table -g -n + +# Check VNet peering status +az network vnet peering list -g --vnet-name --query "[].{name:name, state:peeringState, syncLevel:peeringSyncLevel}" -o table + +# Check VPN connection status +az network vpn-connection show -g -n --query "{status:connectionStatus, inBytes:ingressBytesTransferred, outBytes:egressBytesTransferred}" + +# Check ExpressRoute circuit status +az network express-route show -g -n --query "{state:serviceProviderProvisioningState, circuitState:circuitProvisioningState}" + +# List all NSG flow logs in a region +az network watcher flow-log list --location -o table +``` + +## Further Reading + +- [Network Watcher documentation](https://learn.microsoft.com/azure/network-watcher/network-watcher-overview) +- [Troubleshoot Azure virtual network connectivity](https://learn.microsoft.com/azure/network-watcher/network-watcher-connectivity-overview) +- [NSG diagnostics overview](https://learn.microsoft.com/azure/network-watcher/network-watcher-network-configuration-diagnostics-overview) +- [Effective routes troubleshooting](https://learn.microsoft.com/azure/virtual-network/diagnose-network-routing-problem) diff --git a/plugin/skills/azure-network-troubleshooter/references/connectivity-checklist.md b/plugin/skills/azure-network-troubleshooter/references/connectivity-checklist.md new file mode 100644 index 000000000..9ac91f5c3 --- /dev/null +++ b/plugin/skills/azure-network-troubleshooter/references/connectivity-checklist.md @@ -0,0 +1,257 @@ +# Universal Connectivity Troubleshooting Checklist + +Systematic checklist for diagnosing any Azure network connectivity failure. Work through each step in order — most issues are found in steps 1-3. + +## Prerequisites + +```bash +# Ensure Network Watcher is enabled in the region +az network watcher configure --resource-group NetworkWatcherRG --locations --enabled true + +# Verify you have the right subscription context +az account show --query "{name:name, id:id}" -o table +``` + +## Step 1 — Verify NSGs Allow Traffic (IP Flow Verify) + +NSG misconfigurations are the #1 cause of connectivity failures. Check both source and destination. + +### Check outbound from source + +```bash +az network watcher test-ip-flow \ + --resource-group \ + --vm \ + --direction Outbound \ + --protocol TCP \ + --local ":*" \ + --remote ":" +``` + +### Check inbound at destination + +```bash +az network watcher test-ip-flow \ + --resource-group \ + --vm \ + --direction Inbound \ + --protocol TCP \ + --local ":" \ + --remote ":*" +``` + +### Interpret results + +| Result | Meaning | Action | +|--------|---------|--------| +| `Access: Allow` | NSG permits this flow | NSG is not the problem — continue to Step 2 | +| `Access: Deny`, rule name shown | A specific NSG rule blocks the flow | Check the named rule and its priority | +| `Access: Deny`, `DefaultRule_DenyAllInBound` | No rule allows this inbound flow | Add an allow rule with lower priority number | + +### View all effective NSG rules + +```bash +# Shows the merged result of all NSG rules on a NIC (subnet + NIC level) +az network nic list-effective-nsg \ + --resource-group \ + --name \ + --output json | jq '.value[].effectiveSecurityRules[] | {name, protocol, sourceAddress: .sourceAddressPrefix, destAddress: .destinationAddressPrefix, destPort: .destinationPortRange, access, priority, direction}' +``` + +### Common NSG pitfalls + +- **Subnet NSG + NIC NSG both apply.** Traffic must pass BOTH. A rule allowing traffic on the subnet NSG is useless if the NIC NSG denies it. +- **Priority matters.** Lower number = higher priority. A Deny at priority 100 overrides an Allow at priority 200. +- **Service tags vs. IP addresses.** Verify the service tag covers the expected IP ranges. Use `az network list-service-tags --location ` to check. +- **Return traffic.** NSGs are stateful — if outbound is allowed, the return traffic is automatically permitted. But if using Azure Firewall or an NVA, those may NOT be stateful for all protocols. + +## Step 2 — Check Effective Routes (Next Hop) + +After confirming NSGs allow traffic, verify the traffic is routed correctly. + +### Determine next hop + +```bash +az network watcher show-next-hop \ + --resource-group \ + --vm \ + --source-ip \ + --dest-ip +``` + +### Interpret next hop results + +| Next Hop Type | Meaning | Potential Issue | +|---------------|---------|-----------------| +| `VnetLocal` | Destination is in the same VNet or peered VNet | Expected for VNet traffic — check dest VM/NIC is healthy | +| `Internet` | Traffic routes to the internet | If destination is in Azure, a route is missing | +| `VirtualNetworkGateway` | Traffic routes through VPN/ER gateway | Verify gateway is running and tunnel is up | +| `VirtualAppliance` | Traffic routes through an NVA | Verify NVA is forwarding traffic (IP forwarding enabled) | +| `None` | Traffic is dropped | A UDR with next hop "None" is blackholing this traffic | + +### View full effective route table + +```bash +az network nic show-effective-route-table \ + --resource-group \ + --name \ + --output table +``` + +Look for: +- **Conflicting routes:** Multiple routes for the same prefix with different next hops. Most specific prefix wins. If prefixes are equal, priority is: UDR > BGP > system route. +- **Missing routes:** If the destination subnet has no route, traffic uses the default `0.0.0.0/0` route. +- **Black hole routes:** Routes with next hop `None` explicitly drop traffic. + +## Step 3 — Verify DNS Resolution + +If the destination is specified by hostname (FQDN), DNS must resolve correctly. + +### Test DNS from inside the VM + +```bash +az vm run-command invoke \ + --resource-group \ + --name \ + --command-id RunShellScript \ + --scripts "nslookup && cat /etc/resolv.conf" +``` + +For Windows VMs: + +```bash +az vm run-command invoke \ + --resource-group \ + --name \ + --command-id RunPowerShellScript \ + --scripts "Resolve-DnsName ; Get-DnsClientServerAddress" +``` + +### Check DNS configuration + +```bash +# Check VNet DNS settings +az network vnet show -g -n --query "dhcpOptions.dnsServers" + +# If empty, Azure default DNS (168.63.129.16) is used +# If custom DNS servers are set, verify they are reachable +``` + +For detailed DNS troubleshooting → see [dns-debug.md](dns-debug.md) + +## Step 4 — Test End-to-End Connectivity + +Use Network Watcher connection troubleshoot for a comprehensive path analysis. + +### VM-to-VM connectivity test + +```bash +az network watcher test-connectivity \ + --resource-group \ + --source-resource \ + --dest-resource \ + --dest-port +``` + +### VM-to-external-endpoint test + +```bash +az network watcher test-connectivity \ + --resource-group \ + --source-resource \ + --dest-address \ + --dest-port +``` + +### Interpret connection troubleshoot output + +The output includes: +- **connectionStatus:** `Reachable`, `Unreachable`, or `Unknown` +- **avgLatencyInMs:** Average latency for the connection +- **hops:** Array of hops with issues identified at each + +```bash +# Parse hops for issues +az network watcher test-connectivity \ + --resource-group \ + --source-resource \ + --dest-address 10.0.2.4 \ + --dest-port 443 \ + --query "hops[].{type:type, address:address, issues:issues}" -o json +``` + +## Step 5 — Check Resource Health + +Verify the networking resources themselves are healthy. + +```bash +# Check VPN gateway health +az network vnet-gateway show -g -n --query "provisioningState" + +# Check load balancer backend health +az network lb probe show -g --lb-name -n + +# Check Application Gateway backend health +az network application-gateway show-backend-health -g -n + +# Check ExpressRoute circuit state +az network express-route show -g -n --query "{provisioningState:provisioningState, serviceProviderState:serviceProviderProvisioningState, circuitState:circuitProvisioningState}" +``` + +## Step 6 — Review Activity Logs + +Check if a recent configuration change caused the issue. + +```bash +# Network configuration changes in the last 2 hours +az monitor activity-log list \ + --resource-group \ + --start-time "$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ)" \ + --query "[?contains(operationName.value,'Microsoft.Network') && status.value=='Succeeded'].{operation:operationName.localizedValue, caller:caller, time:eventTimestamp}" \ + --output table + +# Check for failed deployments +az monitor activity-log list \ + --resource-group \ + --start-time "$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ)" \ + --query "[?status.value=='Failed'].{operation:operationName.localizedValue, error:properties.statusMessage, time:eventTimestamp}" \ + --output table +``` + +### Common configuration changes that break connectivity + +- NSG rule added or modified +- Route table associated or dissociated from a subnet +- UDR added or changed +- VNet peering created, deleted, or resynchronized +- DNS server settings changed on VNet +- Firewall rule collection modified +- VPN connection shared key rotated +- Subnet delegation changed + +## Decision Tree Summary + +``` +Connectivity Failure +├── NSG Deny? (Step 1) +│ └── YES → Fix NSG rule (check priority, both subnet+NIC NSGs) +├── Wrong Next Hop? (Step 2) +│ └── YES → Fix route table / UDR / peering / gateway +├── DNS Failure? (Step 3) +│ └── YES → Fix DNS zone / VNet link / resolver config +├── End-to-End Unreachable? (Step 4) +│ └── YES → Analyze hop-by-hop for the failing component +├── Resource Unhealthy? (Step 5) +│ └── YES → Remediate or re-provision the resource +└── Recent Config Change? (Step 6) + └── YES → Correlate change with failure onset and revert if needed +``` + +## Related Skills + +- For detailed NSG analysis → see [nsg-analysis.md](nsg-analysis.md) +- For routing issues → see [routing-debug.md](routing-debug.md) +- For DNS problems → see [dns-debug.md](dns-debug.md) +- For latency issues → see [latency-debug.md](latency-debug.md) +- For VPN-specific troubleshooting → use `azure-vpn-gateway` skill +- For ExpressRoute issues → use `azure-expressroute` skill diff --git a/plugin/skills/azure-network-troubleshooter/references/dns-debug.md b/plugin/skills/azure-network-troubleshooter/references/dns-debug.md new file mode 100644 index 000000000..1e64da5c9 --- /dev/null +++ b/plugin/skills/azure-network-troubleshooter/references/dns-debug.md @@ -0,0 +1,295 @@ +# DNS Resolution Troubleshooting Guide + +Diagnose and fix Azure DNS resolution failures including Private DNS zone issues, split-brain DNS, conditional forwarding failures, and DNS Private Resolver problems. + +## How Azure DNS Resolution Works + +By default, Azure VMs use Azure-provided DNS at `168.63.129.16`. This resolver handles: +- Public DNS resolution (internet names) +- Azure Private DNS zone resolution (if zones are linked to the VNet) +- Azure-internal names (`.internal.cloudapp.net`) + +If custom DNS servers are configured on the VNet, ALL DNS queries go to those servers instead. + +## Step 1 — Identify the DNS Configuration + +```bash +# Check VNet DNS settings +az network vnet show \ + --resource-group \ + --name \ + --query "{dnsServers:dhcpOptions.dnsServers, addressSpace:addressSpace.addressPrefixes}" +``` + +| Result | Meaning | +|--------|---------| +| `dnsServers: []` or `null` | Azure-provided DNS (168.63.129.16) — default | +| `dnsServers: ["10.0.0.4"]` | Custom DNS — all queries go to this server | +| `dnsServers: ["10.0.0.4", "168.63.129.16"]` | Primary custom, fallback to Azure DNS | + +## Step 2 — Test DNS from Inside the VM + +Always test from the VM itself, not from your local machine. + +### Linux VM + +```bash +az vm run-command invoke \ + --resource-group \ + --name \ + --command-id RunShellScript \ + --scripts " + echo '=== resolv.conf ===' + cat /etc/resolv.conf + echo '=== nslookup test ===' + nslookup + echo '=== dig test ===' + dig +short + echo '=== dig with specific server ===' + dig @168.63.129.16 +short + " +``` + +### Windows VM + +```bash +az vm run-command invoke \ + --resource-group \ + --name \ + --command-id RunPowerShellScript \ + --scripts " + Write-Output '=== DNS Client Config ===' + Get-DnsClientServerAddress -AddressFamily IPv4 | Format-Table + Write-Output '=== Resolve-DnsName ===' + Resolve-DnsName '' -ErrorAction SilentlyContinue | Format-Table + Write-Output '=== Test with Azure DNS ===' + Resolve-DnsName '' -Server 168.63.129.16 -ErrorAction SilentlyContinue | Format-Table + " +``` + +## Common DNS Problems + +### Problem: Private DNS Zone Not Resolving + +**Symptom:** `nslookup .privatelink.blob.core.windows.net` returns NXDOMAIN or the public IP instead of the private endpoint IP. + +**Diagnosis:** + +```bash +# Check if the private DNS zone exists +az network private-dns zone show \ + --resource-group \ + --name + +# Check if the zone is linked to the VNet +az network private-dns link vnet list \ + --resource-group \ + --zone-name \ + --output table + +# Check the A record exists +az network private-dns record-set a list \ + --resource-group \ + --zone-name \ + --output table +``` + +**Common causes and fixes:** + +1. **Zone not linked to VNet:** +```bash +az network private-dns link vnet create \ + --resource-group \ + --zone-name \ + --name \ + --virtual-network \ + --registration-enabled false +``` + +2. **A record missing (private endpoint created but DNS record not auto-registered):** +```bash +# Check the private endpoint's network interface for its IP +az network private-endpoint show \ + --resource-group \ + --name \ + --query "customDnsConfigurations[].{fqdn:fqdn, ipAddress:ipAddresses[0]}" + +# Manually create the A record if needed +az network private-dns record-set a add-record \ + --resource-group \ + --zone-name \ + --record-set-name \ + --ipv4-address +``` + +3. **Custom DNS server not forwarding to Azure DNS:** +If the VNet uses a custom DNS server, that server must forward `privatelink.*` zones to `168.63.129.16` for Private DNS zone resolution to work. + +### Problem: Split-Brain DNS + +**Symptom:** A hostname resolves to the public IP from outside Azure but should resolve to the private IP from inside Azure. + +**How it works:** Azure Private DNS zones take precedence over public DNS when linked to the VNet. For example, `storageaccount.blob.core.windows.net` has a CNAME to `storageaccount.privatelink.blob.core.windows.net`. If a Private DNS zone for `privatelink.blob.core.windows.net` is linked to the VNet, the private IP is returned. + +**Diagnosis:** +```bash +# From inside the VM — should return private IP +az vm run-command invoke -g -n \ + --command-id RunShellScript \ + --scripts "dig storageaccount.blob.core.windows.net +short" + +# Check the CNAME chain +az vm run-command invoke -g -n \ + --command-id RunShellScript \ + --scripts "dig storageaccount.blob.core.windows.net +trace" +``` + +**Fix:** Ensure the correct Private DNS zone is linked to the VNet: + +| Azure Service | Private DNS Zone Name | +|---------------|----------------------| +| Blob Storage | `privatelink.blob.core.windows.net` | +| Azure SQL | `privatelink.database.windows.net` | +| Key Vault | `privatelink.vaultcore.azure.net` | +| Azure Container Registry | `privatelink.azurecr.io` | +| Azure Web Apps | `privatelink.azurewebsites.net` | +| Cosmos DB | `privatelink.documents.azure.com` | +| Event Hubs | `privatelink.servicebus.windows.net` | + +Full list: [Azure Private Endpoint DNS configuration](https://learn.microsoft.com/azure/private-link/private-endpoint-dns) + +### Problem: Conditional Forwarding Failures + +**Symptom:** On-premises clients cannot resolve Azure Private DNS zone names. Or Azure VMs cannot resolve on-premises DNS names. + +**Architecture review:** +- Azure VMs use Azure DNS → which resolves Private DNS zones natively +- On-premises clients use on-premises DNS → must forward Azure zones to a DNS resolver in Azure +- The DNS resolver can be: Azure DNS Private Resolver, a custom DNS VM, or Azure Firewall DNS proxy + +**Diagnosis for on-premises → Azure resolution:** + +```bash +# Check if DNS Private Resolver exists and has an inbound endpoint +az dns-resolver inbound-endpoint list \ + --resource-group \ + --dns-resolver-name \ + --output table + +# The inbound endpoint IP is what on-premises DNS should forward to +``` + +**Diagnosis for Azure → on-premises resolution:** + +```bash +# Check if a DNS forwarding ruleset exists +az dns-resolver forwarding-ruleset list \ + --resource-group \ + --output table + +# Check forwarding rules +az dns-resolver forwarding-rule list \ + --resource-group \ + --dns-forwarding-ruleset-name \ + --output table + +# Verify the ruleset is linked to the VNet +az dns-resolver vnet-link list \ + --resource-group \ + --dns-forwarding-ruleset-name \ + --output table +``` + +### Problem: DNS Private Resolver Issues + +**Symptom:** DNS Private Resolver deployed but queries are not being forwarded or resolved. + +**Diagnosis checklist:** + +```bash +# 1. Verify resolver is provisioned successfully +az dns-resolver show \ + --resource-group \ + --name \ + --query "{state:provisioningState, resourceGuid:resourceGuid}" + +# 2. Check inbound endpoints (for receiving queries) +az dns-resolver inbound-endpoint list \ + --resource-group \ + --dns-resolver-name \ + --query "[].{name:name, ip:ipConfigurations[0].privateIpAddress, subnet:ipConfigurations[0].subnet.id}" + +# 3. Check outbound endpoints (for forwarding queries) +az dns-resolver outbound-endpoint list \ + --resource-group \ + --dns-resolver-name \ + --query "[].{name:name, subnet:subnet.id}" + +# 4. Check forwarding rules +az dns-resolver forwarding-rule list \ + --resource-group \ + --dns-forwarding-ruleset-name \ + --query "[].{domain:domainName, targets:targetDnsServers[].ipAddress, state:provisioningState}" +``` + +**Common fixes:** +- Inbound endpoint must be in a **dedicated subnet** (no other resources) +- Outbound endpoint must be in a **different dedicated subnet** +- Forwarding rules must include the trailing dot (e.g., `contoso.com.`) +- Target DNS servers must be reachable from the outbound endpoint's subnet (check NSGs and routing) + +### Problem: Auto-Registration Not Working + +**Symptom:** VM hostname not appearing in the Private DNS zone despite auto-registration being enabled. + +```bash +# Check if the VNet link has registration enabled +az network private-dns link vnet show \ + --resource-group \ + --zone-name \ + --name \ + --query "registrationEnabled" + +# Check registered records +az network private-dns record-set a list \ + --resource-group \ + --zone-name \ + --output table +``` + +**Constraints:** +- Only **one** Private DNS zone per VNet can have auto-registration enabled +- Only VMs in the linked VNet get auto-registered — not PaaS services +- The zone name must be valid (e.g., `contoso.internal`) + +## Diagnostic Commands Quick Reference + +```bash +# Resolve using Azure DNS directly +dig @168.63.129.16 + +# Resolve using a specific DNS server +dig @ + +# Trace the full resolution path +dig +trace + +# Check reverse DNS +dig -x + +# Test DNS over TCP (if UDP is blocked) +dig +tcp + +# Windows equivalents +nslookup +nslookup 168.63.129.16 +Resolve-DnsName -Server 168.63.129.16 +``` + +## Related Resources + +- [Azure DNS Private Resolver](https://learn.microsoft.com/azure/dns/dns-private-resolver-overview) +- [Private endpoint DNS configuration](https://learn.microsoft.com/azure/private-link/private-endpoint-dns) +- [Name resolution for resources in Azure virtual networks](https://learn.microsoft.com/azure/virtual-network/virtual-networks-name-resolution-for-vms-and-role-instances) +- For detailed DNS service configuration → use `azure-dns` skill +- For private endpoint issues → use `azure-private-link` skill diff --git a/plugin/skills/azure-network-troubleshooter/references/latency-debug.md b/plugin/skills/azure-network-troubleshooter/references/latency-debug.md new file mode 100644 index 000000000..46e4f1b5c --- /dev/null +++ b/plugin/skills/azure-network-troubleshooter/references/latency-debug.md @@ -0,0 +1,352 @@ +# Latency and Performance Troubleshooting Guide + +Diagnose and fix Azure network latency issues, packet loss, throughput degradation, and intermittent connectivity using Connection Monitor, Network Watcher, and performance analysis tools. + +## Baseline: Expected Azure Network Latency + +Before troubleshooting, know what's normal: + +| Scenario | Expected Latency | +|----------|-----------------| +| Same Availability Zone | < 1 ms | +| Cross-AZ (same region) | 1–2 ms | +| Cross-region (same geography) | 5–30 ms | +| Cross-geography (e.g., US to Europe) | 70–150 ms | +| VPN Gateway (same region) | 5–15 ms overhead | +| ExpressRoute (private peering) | 1–5 ms overhead from edge | +| ExpressRoute (Microsoft peering) | Variable by POP distance | + +Reference: [Azure network round-trip latency statistics](https://learn.microsoft.com/azure/networking/azure-network-latency) + +## Step 1 — Measure Current Latency + +### Using Connection Monitor + +Connection Monitor provides continuous monitoring and is the primary tool for latency diagnosis. + +```bash +# List existing connection monitors +az network watcher connection-monitor list \ + --location \ + --output table + +# Create a connection monitor to measure latency +az network watcher connection-monitor create \ + --name \ + --location \ + --test-group-name "latency-test" \ + --endpoint-source-name "source-vm" \ + --endpoint-source-resource-id \ + --endpoint-dest-name "destination" \ + --endpoint-dest-address \ + --test-config-name "tcp-test" \ + --protocol Tcp \ + --tcp-port \ + --test-config-threshold-round-trip-time-ms +``` + +### Quick Latency Test from VM + +```bash +# Linux — TCP latency test +az vm run-command invoke -g -n \ + --command-id RunShellScript \ + --scripts " + # Install hping3 if needed: apt-get install -y hping3 + hping3 -S -p -c 10 2>&1 | tail -3 + " + +# Linux — ICMP latency test +az vm run-command invoke -g -n \ + --command-id RunShellScript \ + --scripts "ping -c 20 | tail -5" + +# Windows — TCP latency with Test-NetConnection +az vm run-command invoke -g -n \ + --command-id RunPowerShellScript \ + --scripts " + 1..10 | ForEach-Object { + \$sw = [System.Diagnostics.Stopwatch]::StartNew() + \$result = Test-NetConnection -ComputerName '' -Port -WarningAction SilentlyContinue + \$sw.Stop() + [PSCustomObject]@{Attempt=\$_; Connected=\$result.TcpTestSucceeded; LatencyMs=\$sw.ElapsedMilliseconds} + } | Format-Table + " +``` + +### Using Network Watcher Connection Troubleshoot + +```bash +# Provides hop-by-hop latency analysis +az network watcher test-connectivity \ + --resource-group \ + --source-resource \ + --dest-address \ + --dest-port \ + --query "{status:connectionStatus, latency:avgLatencyInMs, hops:hops[].{type:type, address:address, latency:roundTripTimeMs, issues:issues}}" \ + -o json +``` + +## Step 2 — Identify the Latency Source + +### Check hop-by-hop latency + +From the connection troubleshoot output, look at which hop introduces the most latency: + +| Hop Type | High Latency Cause | +|----------|-------------------| +| `Source` | VM CPU/memory pressure, NIC driver issues | +| `VirtualNetwork` | Unexpected routing through NVA, cross-AZ traffic | +| `VirtualNetworkGateway` | VPN encryption overhead, gateway SKU limits | +| `Internet` | ISP routing, geographic distance | +| `VirtualAppliance` | NVA overloaded, single-NIC bottleneck | + +### Check VM-level performance + +```bash +# Linux — check if the VM itself is the bottleneck +az vm run-command invoke -g -n \ + --command-id RunShellScript \ + --scripts " + echo '=== CPU ===' + top -bn1 | head -5 + echo '=== Network Interface Stats ===' + cat /proc/net/dev + echo '=== TCP Stats ===' + ss -s + echo '=== Dropped Packets ===' + netstat -s | grep -i drop + " +``` + +## Common Latency Problems + +### Problem: NVA/Firewall Bottleneck + +**Symptom:** Latency increases when traffic routes through an NVA or Azure Firewall. Normal when going direct. + +**Diagnosis:** +```bash +# Check if traffic goes through an NVA +az network watcher show-next-hop -g --vm --source-ip --dest-ip + +# If next hop is VirtualAppliance, check NVA metrics +az monitor metrics list \ + --resource \ + --metric "Throughput" "Latency" \ + --interval PT1M \ + --output table +``` + +**Fixes:** +- Scale up the NVA/Firewall SKU +- Use Azure Firewall Premium for hardware-accelerated processing +- Bypass the firewall for trusted traffic (adjust UDRs) +- Use multiple NVA instances behind an internal load balancer + +### Problem: TCP Window Sizing + +**Symptom:** High latency for large data transfers despite low ping times. Throughput is much lower than link capacity. + +**Explanation:** TCP throughput is limited by: `Throughput = Window Size / RTT`. With a 64 KB window and 50 ms RTT, max throughput is ~10 Mbps regardless of available bandwidth. + +**Diagnosis:** +```bash +# Linux — check current TCP window settings +az vm run-command invoke -g -n \ + --command-id RunShellScript \ + --scripts " + echo '=== TCP Window Scaling ===' + sysctl net.ipv4.tcp_window_scaling + echo '=== TCP Buffer Sizes ===' + sysctl net.ipv4.tcp_rmem + sysctl net.ipv4.tcp_wmem + echo '=== Max Buffer ===' + sysctl net.core.rmem_max + sysctl net.core.wmem_max + " +``` + +**Fix (Linux):** +```bash +# Increase TCP buffer sizes for high-latency, high-bandwidth paths +sysctl -w net.ipv4.tcp_window_scaling=1 +sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216" +sysctl -w net.ipv4.tcp_wmem="4096 87380 16777216" +sysctl -w net.core.rmem_max=16777216 +sysctl -w net.core.wmem_max=16777216 +``` + +### Problem: MTU/MSS Issues + +**Symptom:** Small packets work fine, large packets fail or fragment. Connections hang during data transfer after TCP handshake succeeds. Path MTU discovery failures. + +**Azure MTU:** Azure VNets support 1500-byte MTU. VPN tunnels reduce effective MTU due to encapsulation overhead. + +| Path | Effective MTU | +|------|--------------| +| VNet to VNet (same region) | 1500 | +| VNet peering (cross-region) | 1500 | +| VPN Gateway (IPsec) | ~1400 (depends on cipher) | +| ExpressRoute | 1500 | +| VNet to Internet | 1500 (may be lower depending on ISP) | + +**Diagnosis:** +```bash +# Test path MTU (Linux) +az vm run-command invoke -g -n \ + --command-id RunShellScript \ + --scripts " + # Find MTU by sending non-fragmentable packets of decreasing size + for size in 1500 1472 1400 1372 1300; do + echo \"Testing size \$size:\" + ping -M do -s \$size -c 1 2>&1 | grep -E 'bytes from|too long' + done + " +``` + +**Fix:** +```bash +# Clamp MSS on the NIC (prevents fragmentation) +iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + +# Or set a fixed MSS for VPN traffic +iptables -t mangle -A FORWARD -o eth0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360 +``` + +### Problem: ExpressRoute vs VPN Performance + +**Symptom:** Slower-than-expected performance over hybrid connection. + +**ExpressRoute performance checks:** +```bash +# Check ExpressRoute circuit bandwidth utilization +az monitor metrics list \ + --resource \ + --metric "BitsInPerSecond" "BitsOutPerSecond" \ + --interval PT5M \ + --output table + +# Check ExpressRoute gateway connection latency +az monitor metrics list \ + --resource \ + --metric "ExpressRouteGatewayBitsPerSecond" \ + --interval PT5M \ + --output table +``` + +**VPN performance checks:** +```bash +# Check VPN gateway metrics +az monitor metrics list \ + --resource \ + --metric "TunnelBandwidth" "TunnelEgressBytes" "TunnelIngressBytes" \ + --interval PT5M \ + --output table +``` + +**VPN Gateway throughput by SKU:** + +| SKU | Max Throughput | Max Tunnels | +|-----|---------------|-------------| +| VpnGw1 | 650 Mbps | 30 S2S | +| VpnGw2 | 1 Gbps | 30 S2S | +| VpnGw3 | 1.25 Gbps | 30 S2S | +| VpnGw4 | 5 Gbps | 100 S2S | +| VpnGw5 | 10 Gbps | 100 S2S | + +**Fixes:** +- Upgrade VPN Gateway SKU for higher throughput +- Use ExpressRoute instead of VPN for latency-sensitive workloads +- Enable ExpressRoute FastPath for ultra-low latency (bypasses gateway) +- Use multiple VPN tunnels with ECMP for aggregate throughput + +### Problem: Proximity and Region Placement + +**Symptom:** Latency between resources that should be "close" is higher than expected. + +**Diagnosis:** +```bash +# Check VM placement (availability zone) +az vm show -g -n --query "{location:location, zone:zones[0]}" + +# Check if VMs are in the same proximity placement group +az vm show -g -n --query "proximityPlacementGroup.id" +``` + +**Fixes:** +- Use **Proximity Placement Groups** to co-locate VMs in the same datacenter +- Place VMs in the **same Availability Zone** for sub-millisecond latency +- Use **Accelerated Networking** for reduced VM-to-VM latency + +```bash +# Enable Accelerated Networking on a NIC +az network nic update -g -n --accelerated-networking true + +# Verify it's enabled +az network nic show -g -n --query "enableAcceleratedNetworking" +``` + +## Packet Loss Troubleshooting + +### Detect packet loss + +```bash +# Using Connection Monitor (continuous) +az network watcher connection-monitor query \ + --connection-monitor-name \ + --location \ + --query "testResults[].{test:testConfigurationName, loss:checksFailedPercent, latency:roundTripTimeMs}" + +# Quick check from VM (Linux) +az vm run-command invoke -g -n \ + --command-id RunShellScript \ + --scripts "ping -c 100 -i 0.2 | tail -5" +``` + +### Common packet loss causes + +| Cause | Diagnosis | Fix | +|-------|-----------|-----| +| SNAT port exhaustion | `az monitor metrics list --resource --metric "SnatConnectionCount"` | Add NAT Gateway, use more frontend IPs | +| NVA dropping packets | Check NVA CPU > 80%, packet queue overflow | Scale NVA, add load-balanced instances | +| Bandwidth exceeded | Check VM size network limits | Upgrade VM size for higher bandwidth | +| NSG rate limiting | Flow log shows intermittent denies | Review and adjust NSG rules | + +## Packet Capture for Deep Analysis + +```bash +# Start a packet capture +az network watcher packet-capture create \ + --resource-group \ + --vm \ + --name \ + --storage-account \ + --time-limit 60 \ + --filters '[{"protocol":"TCP", "remoteIPAddress":"", "remotePort":""}]' + +# Check capture status +az network watcher packet-capture show \ + --location \ + --name + +# Stop and download +az network watcher packet-capture stop \ + --location \ + --name +``` + +Download the `.cap` file from the storage account and analyze with Wireshark. Look for: +- TCP retransmissions (packet loss) +- TCP window full (throughput bottleneck) +- RST packets (connection resets) +- ICMP unreachable (routing issues) + +## Related Resources + +- [Connection Monitor overview](https://learn.microsoft.com/azure/network-watcher/connection-monitor-overview) +- [Azure network round-trip latency](https://learn.microsoft.com/azure/networking/azure-network-latency) +- [Accelerated Networking overview](https://learn.microsoft.com/azure/virtual-network/accelerated-networking-overview) +- For Network Watcher tools → use `azure-network-watcher` skill +- For VPN performance → use `azure-vpn-gateway` skill +- For ExpressRoute performance → use `azure-expressroute` skill diff --git a/plugin/skills/azure-network-troubleshooter/references/nsg-analysis.md b/plugin/skills/azure-network-troubleshooter/references/nsg-analysis.md new file mode 100644 index 000000000..e615e4ab5 --- /dev/null +++ b/plugin/skills/azure-network-troubleshooter/references/nsg-analysis.md @@ -0,0 +1,261 @@ +# NSG Troubleshooting Guide + +Diagnose and fix Azure Network Security Group (NSG) issues including effective rule analysis, priority conflicts, ASG misconfigurations, and flow log analysis. + +## How NSGs Work + +NSGs contain security rules that allow or deny network traffic. Rules are evaluated by **priority** (lowest number = highest priority). Key concepts: + +- **Two NSG attachment points:** Subnet level and NIC level. Traffic must pass BOTH if both are present. +- **Stateful:** If outbound traffic is allowed, the return inbound traffic is automatically permitted (and vice versa). +- **Default rules:** Cannot be deleted, but can be overridden by rules with lower priority numbers. +- **Processing stops** at the first matching rule. + +### Default Rules (always present, priority 65000+) + +| Priority | Name | Direction | Action | +|----------|------|-----------|--------| +| 65000 | AllowVnetInBound | Inbound | Allow VirtualNetwork → VirtualNetwork | +| 65001 | AllowAzureLoadBalancerInBound | Inbound | Allow AzureLoadBalancer → Any | +| 65500 | DenyAllInBound | Inbound | Deny Any → Any | +| 65000 | AllowVnetOutBound | Outbound | Allow VirtualNetwork → VirtualNetwork | +| 65001 | AllowInternetOutBound | Outbound | Allow Any → Internet | +| 65500 | DenyAllOutBound | Outbound | Deny Any → Any | + +## Step 1 — Check If NSG Is Blocking Traffic + +### IP Flow Verify (fastest check) + +```bash +az network watcher test-ip-flow \ + --resource-group \ + --vm \ + --direction \ + --protocol \ + --local ":" \ + --remote ":" +``` + +The output tells you: +- `Access: Allow` or `Access: Deny` +- The **name of the rule** that made the decision +- Whether it's a subnet-level or NIC-level NSG rule + +### View Effective Security Rules + +```bash +# This shows ALL rules that apply to a NIC (merged from subnet + NIC NSGs) +az network nic list-effective-nsg \ + --resource-group \ + --name \ + --output json +``` + +Parse the output for readability: +```bash +az network nic list-effective-nsg -g -n -o json | \ + jq '.value[].effectiveSecurityRules[] | { + name, + priority, + direction, + access, + protocol, + srcAddr: .sourceAddressPrefix, + srcPort: .sourcePortRange, + dstAddr: .destinationAddressPrefix, + dstPort: .destinationPortRange + }' | jq -s 'sort_by(.priority)' +``` + +## Step 2 — Identify the Problem + +### Problem: Priority Conflicts + +**Symptom:** A rule you created to allow traffic is not working because a higher-priority Deny rule blocks it first. + +```bash +# List all rules sorted by priority +az network nsg rule list \ + --resource-group \ + --nsg-name \ + --output table \ + --query "sort_by([], &priority)" +``` + +**Rules are evaluated in priority order (lowest number first).** A Deny rule at priority 100 blocks traffic even if an Allow rule exists at priority 200. + +**Fix:** Either: +- Delete or modify the conflicting Deny rule +- Create your Allow rule with a lower priority number (higher priority) + +```bash +# Create a higher-priority allow rule +az network nsg rule create \ + --resource-group \ + --nsg-name \ + --name AllowMyTraffic \ + --priority 90 \ + --direction Inbound \ + --access Allow \ + --protocol Tcp \ + --source-address-prefixes \ + --destination-port-ranges \ + --destination-address-prefixes +``` + +### Problem: Subnet NSG + NIC NSG Double Filtering + +**Symptom:** Traffic is allowed by one NSG but blocked by the other. Both must allow the traffic. + +**Traffic flow for inbound:** +``` +Internet → Subnet NSG (inbound rules) → NIC NSG (inbound rules) → VM +``` + +**Traffic flow for outbound:** +``` +VM → NIC NSG (outbound rules) → Subnet NSG (outbound rules) → Internet +``` + +**Diagnosis:** +```bash +# Check which NSGs are attached +az network vnet subnet show -g --vnet-name -n \ + --query "networkSecurityGroup.id" + +az network nic show -g -n \ + --query "networkSecurityGroup.id" + +# Check rules on each NSG separately +az network nsg rule list -g --nsg-name -o table +az network nsg rule list -g --nsg-name -o table +``` + +**Best practice:** Use NSGs at the subnet level only. If you must use both, ensure rules are consistent. + +### Problem: Application Security Group (ASG) Misconfiguration + +**Symptom:** Rules referencing ASGs don't match traffic as expected. + +```bash +# List ASGs and their members +az network asg list -g -o table + +# Check which ASGs a NIC belongs to +az network nic show -g -n \ + --query "ipConfigurations[].applicationSecurityGroups[].id" + +# List NSG rules that reference ASGs +az network nsg rule list -g --nsg-name -o json | \ + jq '.[] | select(.sourceApplicationSecurityGroups != null or .destinationApplicationSecurityGroups != null) | {name, priority, direction, access}' +``` + +**Common ASG pitfalls:** +- The NIC must be in the **same VNet** as other NICs in the ASG +- ASGs can only be used as source OR destination in a rule, **not both** in the same rule (unless both ASGs have members in the same VNet) +- A NIC can belong to multiple ASGs +- Rules with ASG sources/destinations don't appear in effective rules with the ASG name — they expand to IP addresses + +### Problem: Service Tag Not Covering Expected IPs + +**Symptom:** A rule using a service tag (e.g., `Storage`, `AzureCloud`) doesn't match traffic you expect it to match. + +```bash +# List available service tags and their IP ranges +az network list-service-tags --location \ + --query "values[?name==''].properties.addressPrefixes" -o json + +# Common service tags +az network list-service-tags --location \ + --query "values[].name" -o tsv | sort +``` + +**Common issues:** +- Service tags are **region-specific.** `Storage.EastUS` only covers storage in East US. +- Some services use dynamic IPs. The service tag is updated weekly. +- `VirtualNetwork` service tag includes: VNet address space, peered VNet address spaces, on-premises ranges (from VPN/ER), and Azure service endpoint addresses. + +## Step 3 — Analyze NSG Flow Logs + +Flow logs show every connection attempt and whether it was allowed or denied. + +### Enable flow logs + +```bash +# Create a storage account for flow logs (if not already existing) +az storage account create \ + --resource-group \ + --name \ + --location \ + --sku Standard_LRS + +# Enable flow logs (v2 with traffic analytics) +az network watcher flow-log create \ + --resource-group \ + --name \ + --nsg \ + --storage-account \ + --enabled true \ + --format JSON \ + --log-version 2 \ + --retention 30 \ + --traffic-analytics true \ + --workspace +``` + +### Query flow logs via Log Analytics + +```kusto +// Denied flows in the last hour +AzureNetworkAnalytics_CL +| where TimeGenerated > ago(1h) +| where FlowStatus_s == "D" +| project TimeGenerated, SrcIP_s, DestIP_s, DestPort_d, L4Protocol_s, NSGRule_s +| order by TimeGenerated desc +| take 50 + +// Top denied source IPs +AzureNetworkAnalytics_CL +| where TimeGenerated > ago(24h) +| where FlowStatus_s == "D" +| summarize DeniedFlows = count() by SrcIP_s +| order by DeniedFlows desc +| take 20 + +// Allowed flows to a specific destination +AzureNetworkAnalytics_CL +| where TimeGenerated > ago(1h) +| where FlowStatus_s == "A" +| where DestIP_s == "" +| project TimeGenerated, SrcIP_s, DestPort_d, L4Protocol_s, NSGRule_s +| order by TimeGenerated desc +``` + +### Common "Traffic Denied" Patterns + +| Pattern | Likely Cause | Fix | +|---------|-------------|-----| +| Denied by `DefaultRule_DenyAllInBound` | No allow rule exists for this inbound traffic | Create an explicit allow rule | +| Denied by `DefaultRule_DenyAllOutBound` | No allow rule exists for this outbound traffic | Create an explicit outbound allow rule (rare — default allows internet) | +| Denied by a named rule | An explicit deny rule is blocking traffic | Check if the deny rule is too broad or if your allow rule has lower priority | +| Allowed by subnet NSG, denied by NIC NSG | Double-NSG filtering | Add matching rule to NIC NSG or remove NIC-level NSG | +| Allowed by NSG but connection still fails | Problem is NOT the NSG — check routing, DNS, firewall, or destination service | Continue to routing or DNS troubleshooting | + +## NSG Best Practices + +1. **Use subnet-level NSGs** as the primary enforcement point. Avoid NIC-level NSGs unless you need per-VM differentiation. +2. **Use Application Security Groups** to group VMs by role instead of managing IP-based rules. +3. **Leave a gap between priority numbers** (e.g., 100, 200, 300) to make inserting new rules easy. +4. **Use service tags** instead of hard-coded IP addresses wherever possible. +5. **Enable flow logs** on all NSGs for auditing and troubleshooting. +6. **Document NSG rules** with descriptive names and the `--description` field. +7. **Avoid "allow all" rules** (`*` source, `*` port) — be as specific as possible. + +## Related Resources + +- [Network security groups overview](https://learn.microsoft.com/azure/virtual-network/network-security-groups-overview) +- [NSG flow logs overview](https://learn.microsoft.com/azure/network-watcher/nsg-flow-logs-overview) +- [Application security groups](https://learn.microsoft.com/azure/virtual-network/application-security-groups) +- [Service tags overview](https://learn.microsoft.com/azure/virtual-network/service-tags-overview) +- For firewall-level filtering → use `azure-firewall` skill +- For VNet and subnet management → use `azure-virtual-network` skill diff --git a/plugin/skills/azure-network-troubleshooter/references/routing-debug.md b/plugin/skills/azure-network-troubleshooter/references/routing-debug.md new file mode 100644 index 000000000..baa77b53a --- /dev/null +++ b/plugin/skills/azure-network-troubleshooter/references/routing-debug.md @@ -0,0 +1,282 @@ +# Routing Troubleshooting Guide + +Diagnose and fix Azure network routing issues including UDR conflicts, asymmetric routing, BGP route propagation failures, and unexpected next hops. + +## How Azure Routing Works + +Azure uses a routing precedence hierarchy. When multiple routes match a destination, the most specific prefix wins. For equal prefixes: + +1. **User-Defined Routes (UDR)** — highest priority +2. **BGP routes** — from VPN Gateway or ExpressRoute +3. **System routes** — Azure default routes + +## Gathering Routing Information + +### View effective routes on a NIC + +```bash +az network nic show-effective-route-table \ + --resource-group \ + --name \ + --output table +``` + +This shows the MERGED result of system routes, UDRs, and BGP routes. This is the ground truth for what the VM actually uses. + +### View the route table attached to a subnet + +```bash +# Find which route table is attached +az network vnet subnet show \ + --resource-group \ + --vnet-name \ + --name \ + --query "routeTable.id" + +# List routes in the table +az network route-table route list \ + --resource-group \ + --route-table-name \ + --output table +``` + +### Check next hop for a specific destination + +```bash +az network watcher show-next-hop \ + --resource-group \ + --vm \ + --source-ip \ + --dest-ip +``` + +## Common Routing Problems + +### Problem: UDR Conflicts + +**Symptom:** Traffic goes to an unexpected destination after adding or modifying a UDR. + +**Diagnosis:** + +```bash +# View effective routes and look for conflicting entries +az network nic show-effective-route-table -g -n -o json | \ + jq '.value[] | {prefix: .addressPrefix[], nextHop: .nextHopIpAddress[], nextHopType: .nextHopType, source: .source}' +``` + +**Common causes:** +- A broader UDR (`10.0.0.0/8`) overriding what you expected from a more specific system route — but check: longest prefix match still applies, so `/16` beats `/8`. +- Multiple route tables with conflicting routes on different subnets in the same VNet. +- A UDR pointing to an NVA IP that is down or does not have IP forwarding enabled. + +**Fix:** +```bash +# Update a route to fix the next hop +az network route-table route update \ + --resource-group \ + --route-table-name \ + --name \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address + +# Or remove a conflicting route +az network route-table route delete \ + --resource-group \ + --route-table-name \ + --name +``` + +### Problem: Asymmetric Routing + +**Symptom:** Outbound traffic follows one path, return traffic follows a different path. Causes connection failures with stateful firewalls/NVAs. + +**Diagnosis:** +1. Check effective routes on BOTH source and destination VMs +2. If an NVA or Azure Firewall is in the path, verify the return traffic also traverses it + +```bash +# Check route from source to destination +az network watcher show-next-hop -g --vm --source-ip --dest-ip + +# Check route from destination back to source +az network watcher show-next-hop -g --vm --source-ip --dest-ip +``` + +**Common causes:** +- UDR on the source subnet routes traffic through a firewall, but the destination subnet has no UDR routing return traffic through the same firewall. +- VPN/ExpressRoute gateway subnet has BGP routes that create asymmetric return paths. +- Load balancer with Direct Server Return (DSR) creates asymmetric flows by design. + +**Fix:** Ensure UDRs are applied symmetrically. Both subnets involved in a flow must route through the same firewall/NVA: + +```bash +# Create return-path route on the destination subnet's route table +az network route-table route create \ + --resource-group \ + --route-table-name \ + --name return-via-firewall \ + --address-prefix \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address +``` + +### Problem: Route Propagation from VPN/ExpressRoute Gateway + +**Symptom:** On-premises routes not appearing in VNet effective routes, or stale routes persisting after changes. + +**Diagnosis:** + +```bash +# Check if BGP route propagation is enabled on the subnet +az network vnet subnet show \ + --resource-group \ + --vnet-name \ + --name \ + --query "routeTable.properties.disableBgpRoutePropagation" +# false = propagation enabled (default), true = disabled + +# Check BGP routes learned by the gateway +az network vnet-gateway list-learned-routes \ + --resource-group \ + --name \ + --output table + +# Check BGP routes advertised to a peer +az network vnet-gateway list-advertised-routes \ + --resource-group \ + --name \ + --peer \ + --output table +``` + +**Common causes:** +- `disableBgpRoutePropagation` set to `true` on the route table — BGP routes from the gateway won't reach subnets using that route table. +- BGP session is down between the gateway and peer. +- On-premises device is not advertising the expected prefixes. +- A UDR with the same prefix overrides the BGP route. + +**Fix:** +```bash +# Re-enable BGP route propagation on a route table +az network route-table update \ + --resource-group \ + --name \ + --disable-bgp-route-propagation false +``` + +### Problem: VNet Peering Route Issues + +**Symptom:** VMs in peered VNets cannot communicate, or on-premises routes are not transiting through peering. + +**Diagnosis:** + +```bash +# Check peering status (must be "Connected" on both sides) +az network vnet peering show \ + --resource-group \ + --vnet-name \ + --name \ + --query "{state:peeringState, allowForwardedTraffic:allowForwardedTraffic, allowGatewayTransit:allowGatewayTransit, useRemoteGateways:useRemoteGateways}" + +# Verify both sides +az network vnet peering list -g --vnet-name -o table +az network vnet peering list -g --vnet-name -o table +``` + +**Transitive routing through peering is NOT automatic.** VNet A peered with VNet B, and VNet B peered with VNet C, does NOT mean A can reach C. Options: +- Use Azure Virtual WAN (provides transitive routing) +- Use Azure Route Server with an NVA +- Use UDRs with an NVA to forward traffic + +**Gateway transit settings:** +- Hub VNet (with gateway): Set `allowGatewayTransit: true` +- Spoke VNet: Set `useRemoteGateways: true` + +```bash +# Enable gateway transit on the hub side +az network vnet peering update \ + --resource-group \ + --vnet-name \ + --name \ + --set allowGatewayTransit=true + +# Enable use of remote gateway on the spoke side +az network vnet peering update \ + --resource-group \ + --vnet-name \ + --name \ + --set useRemoteGateways=true +``` + +### Problem: BGP Route Troubleshooting + +**Symptom:** BGP peering is established but expected routes are missing or incorrect. + +```bash +# Check BGP peer status +az network vnet-gateway list-bgp-peer-status \ + --resource-group \ + --name \ + --output table + +# Check learned routes (what the gateway learned from peers) +az network vnet-gateway list-learned-routes \ + --resource-group \ + --name \ + --output table + +# Check advertised routes (what the gateway tells its peers) +az network vnet-gateway list-advertised-routes \ + --resource-group \ + --name \ + --peer \ + --output table +``` + +**BGP route selection (when multiple paths exist):** +1. Longest prefix match +2. Shortest AS path +3. Lowest origin type (IGP < EGP < Incomplete) +4. Lowest MED +5. eBGP over iBGP +6. Lowest router ID + +### Problem: Traffic Being Black-Holed + +**Symptom:** Traffic silently disappears. No ICMP unreachable, no timeout — just no response. + +**Diagnosis:** +```bash +# Look for routes with next hop "None" +az network nic show-effective-route-table -g -n -o json | \ + jq '.value[] | select(.nextHopType == "None") | {prefix: .addressPrefix[], source: .source}' +``` + +**Common causes:** +- UDR with next hop type "None" — intentional black hole (used to prevent traffic to specific destinations) +- UDR pointing to an NVA that is down +- NVA without IP forwarding enabled on its NIC + +```bash +# Verify IP forwarding is enabled on the NVA NIC +az network nic show -g -n --query "enableIpForwarding" + +# Enable IP forwarding if disabled +az network nic update -g -n --ip-forwarding true +``` + +## Route Table Best Practices + +- **One route table per subnet role.** Don't share route tables between subnets with different routing requirements. +- **Document every UDR.** Use the `--name` field descriptively (e.g., `to-hub-firewall`, `block-internet`). +- **Test after changes.** Always run `show-next-hop` after modifying routes. +- **Be explicit about return paths.** If you route traffic through a firewall in one direction, route the return path through the same firewall. +- **Avoid overlapping prefixes** in UDRs unless you intentionally need longest-prefix-match behavior. + +## Related Resources + +- [Virtual network traffic routing](https://learn.microsoft.com/azure/virtual-network/virtual-networks-udr-overview) +- [Diagnose a VM network routing problem](https://learn.microsoft.com/azure/virtual-network/diagnose-network-routing-problem) +- [BGP with Azure VPN Gateway](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-bgp-overview) +- For VPN-specific routing → use `azure-vpn-gateway` skill +- For Virtual WAN routing → use `azure-virtual-wan` skill diff --git a/plugin/skills/azure-network-watcher/SKILL.md b/plugin/skills/azure-network-watcher/SKILL.md new file mode 100644 index 000000000..1fe8f1ce8 --- /dev/null +++ b/plugin/skills/azure-network-watcher/SKILL.md @@ -0,0 +1,189 @@ +--- +name: azure-network-watcher +description: "Diagnose, monitor, and troubleshoot Azure network issues using Network Watcher capabilities including packet capture, NSG flow logs, IP flow verify, next hop, connection troubleshoot, VPN troubleshoot, topology, and connection monitor. WHEN: network watcher, packet capture, flow logs, NSG flow logs, connection troubleshoot, IP flow verify, next hop, VPN troubleshoot, network topology, NSG diagnostics, connection monitor. DO NOT USE FOR: Azure Monitor metrics/logs (use general monitoring), Application Insights (use appinsights-instrumentation), resource health checks (use azure-diagnostics)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Network Watcher + +Azure Network Watcher is a regional service that provides network monitoring and diagnostic tools for Azure IaaS resources. It enables you to monitor, diagnose, and gain insights into your network health and performance. + +## When to Use This Skill + +- Capturing and analyzing network packets on a VM to diagnose connectivity or application issues +- Enabling and querying NSG flow logs to understand traffic patterns and detect anomalies +- Verifying whether a specific network flow is allowed or denied by NSG rules (IP flow verify) +- Determining the next hop for traffic from a VM to identify routing problems +- Troubleshooting VPN gateway connections and site-to-site tunnel failures +- Running connection troubleshoot to test reachability between a source VM and a destination +- Setting up Connection Monitor to continuously test connectivity to Azure, on-premises, or external endpoints +- Viewing network topology for a resource group or virtual network +- Running NSG diagnostics to evaluate effective security rules on a NIC or subnet + +## Rules + +1. Network Watcher must be enabled in every region where you have IaaS resources. Azure automatically enables it when you create or update a virtual network in a subscription, but verify it exists before running diagnostics. +2. Always specify the correct Network Watcher region — diagnostic operations run against the Network Watcher in the same region as the target resource. +3. Packet captures require the Network Watcher VM extension installed on the target VM. Install it before attempting a capture. +4. NSG flow logs v2 are preferred over v1 — they include throughput information needed for Traffic Analytics. +5. Connection Monitor replaces the legacy Connection Monitor (classic) and Network Performance Monitor. Always use Connection Monitor v2 for new setups. +6. IP flow verify checks NSG rules only — it does not evaluate Azure Firewall rules, NVA rules, or route tables. +7. Next hop identifies the next hop type from Azure route tables — verify UDR configuration separately if the next hop is unexpected. +8. Store packet captures and flow logs in a storage account in the same region as the Network Watcher to avoid cross-region data transfer costs. +9. VPN troubleshoot operations can take several minutes to complete — do not assume immediate results. +10. The VM agent must be running and healthy for packet capture and connection troubleshoot to work. + +## MCP Tools + +| Tool | Method | Purpose | +|------|--------|---------| +| `azure__network` | `network_watcher_list` | List Network Watcher instances across subscriptions and regions | + +> **Note:** Most Network Watcher diagnostic operations (packet capture, IP flow verify, next hop, connection troubleshoot, VPN troubleshoot) require CLI commands. Use the MCP tool to discover and verify Network Watcher instances, then use CLI for diagnostic operations. + +## CLI Fallback + +```bash +# List Network Watcher instances +az network watcher list --output table + +# Enable Network Watcher in a region +az network watcher configure --resource-group NetworkWatcherRG --locations eastus --enabled true + +# --- IP Flow Verify --- +az network watcher test-ip-flow \ + --resource-group myRG \ + --vm myVM \ + --direction Inbound \ + --protocol TCP \ + --local 10.0.0.4:* \ + --remote 10.1.0.4:443 + +# --- Next Hop --- +az network watcher show-next-hop \ + --resource-group myRG \ + --vm myVM \ + --source-ip 10.0.0.4 \ + --dest-ip 10.1.0.4 + +# --- Packet Capture --- +# Install the Network Watcher extension on the VM first +az vm extension set \ + --resource-group myRG \ + --vm-name myVM \ + --name NetworkWatcherAgentLinux \ + --publisher Microsoft.Azure.NetworkWatcher + +# Create a packet capture +az network watcher packet-capture create \ + --resource-group myRG \ + --vm myVM \ + --name myCapture \ + --storage-account myStorageAccount \ + --time-limit 300 \ + --filters '[{"protocol":"TCP","localIPAddress":"10.0.0.4","localPort":"443"}]' + +# List packet captures +az network watcher packet-capture list --location eastus --output table + +# Stop a packet capture +az network watcher packet-capture stop --location eastus --name myCapture + +# Show packet capture status +az network watcher packet-capture show-status --location eastus --name myCapture + +# --- NSG Flow Logs --- +# Create NSG flow log (v2 with Traffic Analytics) +az network watcher flow-log create \ + --resource-group myRG \ + --name myFlowLog \ + --nsg myNSG \ + --storage-account myStorageAccount \ + --enabled true \ + --format JSON \ + --log-version 2 \ + --retention 90 \ + --traffic-analytics true \ + --workspace myLogAnalyticsWorkspace + +# List flow logs +az network watcher flow-log list --location eastus --output table + +# Show flow log configuration +az network watcher flow-log show --location eastus --name myFlowLog + +# --- Connection Troubleshoot --- +az network watcher test-connectivity \ + --resource-group myRG \ + --source-resource myVM \ + --dest-address 10.1.0.4 \ + --dest-port 443 \ + --protocol TCP + +# --- VPN Troubleshoot --- +az network watcher troubleshooting start \ + --resource-group myRG \ + --resource myVPNGateway \ + --resource-type vpnGateway \ + --storage-account myStorageAccount \ + --storage-path "https://mystorage.blob.core.windows.net/troubleshoot" + +# Show VPN troubleshoot results +az network watcher troubleshooting show \ + --resource-group myRG \ + --resource myVPNGateway \ + --resource-type vpnGateway + +# --- Connection Monitor --- +az network watcher connection-monitor create \ + --name myMonitor \ + --location eastus \ + --test-group-name myTestGroup \ + --endpoint-source-name myVM \ + --endpoint-source-resource-id "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/myVM" \ + --endpoint-dest-name myDest \ + --endpoint-dest-address 10.1.0.4 \ + --test-config-name tcpTest \ + --protocol Tcp \ + --tcp-port 443 + +# List connection monitors +az network watcher connection-monitor list --location eastus --output table + +# --- Topology --- +az network watcher show-topology \ + --resource-group myRG \ + --location eastus + +# --- NSG Diagnostics --- +az network watcher run-configuration-diagnostic \ + --resource "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkInterfaces/myNIC" \ + --direction Inbound \ + --profiles '[{"direction":"Inbound","protocol":"TCP","source":"10.0.0.4","destination":"10.0.1.4","destinationPort":"443"}]' +``` + +## Key Concepts + +- **Network Watcher** is a regional service — there is one instance per region per subscription, automatically created in the `NetworkWatcherRG` resource group. +- **IP Flow Verify** tests whether a packet is allowed or denied by NSG rules. It evaluates both the NIC-level and subnet-level NSGs. It returns the specific rule name that allows or denies the flow. +- **Next Hop** returns the next hop type (VirtualNetwork, Internet, VirtualAppliance, VNetPeering, None) and the next hop IP address. Use it to diagnose asymmetric routing, black-holed traffic, or misconfigured UDRs. +- **Packet Capture** runs on a VM using the Network Watcher extension. Captures can be stored in a storage account or locally on the VM. Use capture filters to reduce noise and file size. +- **NSG Flow Logs** record information about IP traffic flowing through NSGs. Version 2 logs add byte and packet counts per flow, which are required for Traffic Analytics. +- **Traffic Analytics** processes NSG flow log data in a Log Analytics workspace to provide flow visualization, top talkers, security insights, and geo-mapping of traffic. +- **Connection Troubleshoot** performs a connectivity check from a source VM to a destination and returns the full hop-by-hop path along with issues detected at each hop. +- **VPN Troubleshoot** diagnoses VPN gateway and connection health, checking tunnel status, SA lifetime, routing configuration, and certificate validity. +- **Connection Monitor** provides continuous, agent-based end-to-end connectivity monitoring. It supports TCP, HTTP, and ICMP test protocols with configurable thresholds and alerting. +- **Topology** produces a visual or JSON representation of network resources and their relationships within a resource group or VNet. + +## References + +- [Connection Monitor setup and configuration](references/connection-monitor.md) +- [Packet capture creation and analysis](references/packet-capture.md) +- [NSG flow logs and Traffic Analytics](references/nsg-flow-logs.md) +- [IP flow verify diagnostic](references/ip-flow-verify.md) +- [Next hop diagnostic](references/next-hop.md) +- [Network Watcher overview — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/network-watcher-monitoring-overview) +- [Network Watcher FAQ — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/frequently-asked-questions) diff --git a/plugin/skills/azure-network-watcher/references/connection-monitor.md b/plugin/skills/azure-network-watcher/references/connection-monitor.md new file mode 100644 index 000000000..089bf0a49 --- /dev/null +++ b/plugin/skills/azure-network-watcher/references/connection-monitor.md @@ -0,0 +1,189 @@ +# Connection Monitor + +Connection Monitor provides continuous, end-to-end connectivity monitoring between source endpoints (Azure VMs, on-premises machines) and destination endpoints (Azure resources, external URLs, IP addresses). It replaces the legacy Connection Monitor (classic) and Network Performance Monitor. + +## Architecture + +Connection Monitor uses a hierarchical structure: + +- **Connection Monitor** — the top-level resource, scoped to a region +- **Test Groups** — logical groupings of sources, destinations, and test configurations +- **Endpoints** — source and destination resources (VMs, IP addresses, URLs, Azure resources) +- **Test Configurations** — protocol settings, frequency, and success thresholds + +A single connection monitor can contain multiple test groups, allowing you to monitor different connectivity scenarios from one resource. + +## Supported Source Endpoints + +| Source Type | Requirements | +|-------------|-------------| +| Azure VM | Network Watcher extension installed | +| Azure VM Scale Set | Network Watcher extension on instances | +| On-premises machine | Azure Monitor Agent installed, connected via Log Analytics workspace | +| Azure Arc-enabled server | Azure Monitor Agent installed | + +## Supported Destination Endpoints + +| Destination Type | Example | +|------------------|---------| +| Azure VM | VM resource ID | +| External URL | `https://www.microsoft.com` | +| IP address | `10.0.1.4` or `203.0.113.1` | +| Azure resource | Storage account, SQL Database, App Service | + +## Test Protocols + +### TCP Test + +```bash +az network watcher connection-monitor create \ + --name myMonitor \ + --location eastus \ + --test-group-name webServerGroup \ + --endpoint-source-name sourceVM \ + --endpoint-source-resource-id "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/sourceVM" \ + --endpoint-dest-name destVM \ + --endpoint-dest-resource-id "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/destVM" \ + --test-config-name tcpTest \ + --protocol Tcp \ + --tcp-port 443 \ + --tcp-disable-trace-route false \ + --frequency 30 +``` + +TCP tests check if a TCP connection can be established on the specified port. They report round-trip time and connection success/failure. + +### HTTP Test + +```bash +az network watcher connection-monitor test-configuration add \ + --connection-monitor myMonitor \ + --location eastus \ + --name httpTest \ + --protocol Http \ + --http-port 443 \ + --http-method GET \ + --http-path "/" \ + --http-valid-status-codes 200 301 302 \ + --frequency 60 +``` + +HTTP tests send HTTP requests and validate the response status code. They report latency, response time, and whether the returned status code is in the valid set. + +### ICMP Test + +```bash +az network watcher connection-monitor test-configuration add \ + --connection-monitor myMonitor \ + --location eastus \ + --name icmpTest \ + --protocol Icmp \ + --frequency 60 +``` + +ICMP tests use ping to check basic reachability. Note that ICMP may be blocked by NSGs or firewalls even if TCP connectivity works. + +## Thresholds and Alerts + +Connection Monitor supports configurable thresholds for alerting: + +- **Round-trip time threshold** — alert when latency exceeds a specified value in milliseconds +- **Checks failed percentage** — alert when the percentage of failed checks exceeds a threshold + +```bash +# Add a test configuration with thresholds +az network watcher connection-monitor test-configuration add \ + --connection-monitor myMonitor \ + --location eastus \ + --name criticalTest \ + --protocol Tcp \ + --tcp-port 1433 \ + --frequency 30 \ + --threshold-failed-percent 10 \ + --threshold-round-trip-time 100 +``` + +To integrate with Azure Monitor alerts, create a metric alert on the Connection Monitor metrics: + +```bash +# Create an alert rule for connection monitor check failures +az monitor metrics alert create \ + --name "ConnectionMonitorAlert" \ + --resource-group myRG \ + --scopes "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkWatchers/NetworkWatcher_eastus/connectionMonitors/myMonitor" \ + --condition "avg ChecksFailedPercent > 20" \ + --window-size 5m \ + --evaluation-frequency 1m \ + --action "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Insights/actionGroups/myActionGroup" +``` + +## Monitoring On-Premises to Azure + +To monitor connectivity from on-premises machines to Azure resources: + +1. Install the Azure Monitor Agent on the on-premises machine +2. Connect the machine to a Log Analytics workspace +3. Add the on-premises machine as a source endpoint using its Log Analytics workspace ID + +```bash +# Create a connection monitor with an on-premises source +az network watcher connection-monitor create \ + --name onPremToAzure \ + --location eastus \ + --test-group-name hybridGroup \ + --endpoint-source-name onPremServer \ + --endpoint-source-type ExternalAddress \ + --endpoint-source-address "192.168.1.10" \ + --endpoint-dest-name azureVM \ + --endpoint-dest-resource-id "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/destVM" \ + --test-config-name tcpTest \ + --protocol Tcp \ + --tcp-port 3389 \ + --frequency 60 +``` + +## Managing Connection Monitors + +```bash +# List all connection monitors in a region +az network watcher connection-monitor list --location eastus --output table + +# Show details of a specific monitor +az network watcher connection-monitor show --location eastus --name myMonitor + +# Query test results +az network watcher connection-monitor query --location eastus --name myMonitor + +# Stop a connection monitor (pause monitoring) +az network watcher connection-monitor stop --location eastus --name myMonitor + +# Start a paused connection monitor +az network watcher connection-monitor start --location eastus --name myMonitor + +# Delete a connection monitor +az network watcher connection-monitor delete --location eastus --name myMonitor +``` + +## Topology Considerations + +| Scenario | Recommended Test Protocol | Frequency | +|----------|--------------------------|-----------| +| Web app availability | HTTP (check status codes) | 30–60 seconds | +| Database connectivity | TCP on port 1433/3306/5432 | 30 seconds | +| VPN tunnel health | TCP or ICMP | 60 seconds | +| DNS resolution | TCP on port 53 | 60 seconds | +| General reachability | ICMP | 60–300 seconds | + +## Quotas and Limits + +- Maximum 100 connection monitors per subscription per region +- Maximum 20 test groups per connection monitor +- Maximum 100 endpoints (sources and destinations combined) per connection monitor +- Maximum 25 test configurations per connection monitor +- Test frequency minimum: 30 seconds for TCP/HTTP, 60 seconds for ICMP + +## Learn More + +- [Connection Monitor overview — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/connection-monitor-overview) +- [Create a Connection Monitor — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/connection-monitor-create-using-portal) +- [Migrate to Connection Monitor from Network Performance Monitor — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/migrate-to-connection-monitor-from-network-performance-monitor) diff --git a/plugin/skills/azure-network-watcher/references/ip-flow-verify.md b/plugin/skills/azure-network-watcher/references/ip-flow-verify.md new file mode 100644 index 000000000..828d3a718 --- /dev/null +++ b/plugin/skills/azure-network-watcher/references/ip-flow-verify.md @@ -0,0 +1,198 @@ +# IP Flow Verify + +IP flow verify tests whether a packet is allowed or denied to or from a virtual machine based on network security group (NSG) rules. It identifies the specific NSG rule that allows or blocks the traffic, making it the fastest way to diagnose NSG-related connectivity issues. + +## How It Works + +IP flow verify evaluates the effective NSG rules applied to a VM's network interface. It checks: + +1. NSG rules at the **NIC level** (if an NSG is associated with the NIC) +2. NSG rules at the **subnet level** (if an NSG is associated with the subnet) + +It returns the result (Allow or Deny) and the name of the specific rule that matched. If no rule matches, the default rules apply (DenyAllInbound for inbound, AllowInternetOutbound for outbound). + +> **Important:** IP flow verify evaluates NSG rules only. It does not check Azure Firewall rules, NVA (Network Virtual Appliance) rules, route tables, or application-level firewalls. + +## Required Parameters + +| Parameter | Description | Example | +|-----------|-------------|---------| +| `--vm` | Target VM name | `myVM` | +| `--resource-group` | Resource group of the VM | `myRG` | +| `--direction` | Traffic direction | `Inbound` or `Outbound` | +| `--protocol` | Network protocol | `TCP`, `UDP`, or `*` | +| `--local` | Local (VM) IP and port | `10.0.0.4:443` or `10.0.0.4:*` | +| `--remote` | Remote IP and port | `10.1.0.4:49152` or `10.1.0.4:*` | + +The `--local` parameter refers to the VM being tested. The `--remote` parameter refers to the other endpoint. + +## CLI Usage + +### Test inbound TCP traffic to port 443 + +```bash +az network watcher test-ip-flow \ + --resource-group myRG \ + --vm myVM \ + --direction Inbound \ + --protocol TCP \ + --local 10.0.0.4:443 \ + --remote 203.0.113.10:49152 +``` + +**Output (allowed):** +```json +{ + "access": "Allow", + "ruleName": "AllowHTTPS" +} +``` + +**Output (denied):** +```json +{ + "access": "Deny", + "ruleName": "DefaultRule_DenyAllInBound" +} +``` + +### Test outbound traffic to the internet + +```bash +az network watcher test-ip-flow \ + --resource-group myRG \ + --vm myVM \ + --direction Outbound \ + --protocol TCP \ + --local 10.0.0.4:* \ + --remote 8.8.8.8:443 +``` + +### Test inbound SSH access + +```bash +az network watcher test-ip-flow \ + --resource-group myRG \ + --vm myVM \ + --direction Inbound \ + --protocol TCP \ + --local 10.0.0.4:22 \ + --remote 198.51.100.5:49152 +``` + +### Test UDP traffic (DNS) + +```bash +az network watcher test-ip-flow \ + --resource-group myRG \ + --vm myVM \ + --direction Outbound \ + --protocol UDP \ + --local 10.0.0.4:* \ + --remote 168.63.129.16:53 +``` + +### Test inbound traffic from a VNet peer + +```bash +az network watcher test-ip-flow \ + --resource-group myRG \ + --vm myVM \ + --direction Inbound \ + --protocol TCP \ + --local 10.0.0.4:1433 \ + --remote 10.1.0.4:49152 +``` + +## Interpreting Results + +### Access Values + +| Result | Meaning | +|--------|---------| +| `Allow` | Traffic is permitted by the identified NSG rule | +| `Deny` | Traffic is blocked by the identified NSG rule | + +### Common Rule Names + +| Rule Name Pattern | Meaning | +|-------------------|---------| +| `AllowVnetInBound` | Default rule: allows all inbound traffic within the VNet | +| `AllowAzureLoadBalancerInBound` | Default rule: allows Azure Load Balancer health probes | +| `DenyAllInBound` | Default rule: denies all other inbound traffic | +| `AllowVnetOutBound` | Default rule: allows all outbound traffic within the VNet | +| `AllowInternetOutBound` | Default rule: allows outbound traffic to the internet | +| `DenyAllOutBound` | Default rule: denies all other outbound traffic | +| Custom names (e.g., `AllowHTTPS`, `DenySSH`) | User-defined NSG rules | + +### Default Rule Evaluation + +If the result shows a `DefaultRule_*` rule, it means no user-defined rule matched the traffic. Default rules have the lowest priority (65000–65500) and serve as catch-all rules. + +## Diagnostic Scenarios + +### Scenario 1: VM cannot receive web traffic + +```bash +# Test inbound HTTPS +az network watcher test-ip-flow \ + --resource-group myRG --vm webVM \ + --direction Inbound --protocol TCP \ + --local 10.0.0.4:443 --remote 0.0.0.0:49152 +``` + +If result is `Deny` with `DefaultRule_DenyAllInBound`, you need to create an NSG rule allowing inbound TCP 443. + +### Scenario 2: VM cannot connect to a database + +```bash +# Test outbound to SQL Server +az network watcher test-ip-flow \ + --resource-group myRG --vm appVM \ + --direction Outbound --protocol TCP \ + --local 10.0.0.4:* --remote 10.1.0.4:1433 +``` + +If result is `Deny`, check the source VM's outbound NSG rules. Also test inbound on the database VM: + +```bash +az network watcher test-ip-flow \ + --resource-group myRG --vm dbVM \ + --direction Inbound --protocol TCP \ + --local 10.1.0.4:1433 --remote 10.0.0.4:49152 +``` + +### Scenario 3: Verify that SSH is blocked from the internet + +```bash +az network watcher test-ip-flow \ + --resource-group myRG --vm myVM \ + --direction Inbound --protocol TCP \ + --local 10.0.0.4:22 --remote 203.0.113.1:49152 +``` + +If result is `Deny`, SSH is properly blocked. If `Allow`, identify the rule and evaluate whether it should be removed. + +## Limitations + +- Only evaluates NSG rules — does not account for Azure Firewall, NVAs, or route-based filtering +- Cannot test traffic that bypasses NSGs (e.g., traffic to service endpoints, private endpoints) +- Requires the VM to be in a running state +- Tests a single flow at a time — for bulk analysis, use NSG diagnostics instead +- The IP addresses must be valid for the VM's NIC configuration + +## When to Use IP Flow Verify vs Other Tools + +| Tool | Use When | +|------|----------| +| **IP flow verify** | Quick check: is traffic allowed or denied by NSGs? | +| **NSG diagnostics** | Evaluate multiple flows or get effective security rules | +| **Connection troubleshoot** | Full end-to-end connectivity test including routing | +| **Next hop** | Determine where traffic is being routed | +| **Effective security rules** | View all effective NSG rules on a NIC | + +## Learn More + +- [IP flow verify overview — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/ip-flow-verify-overview) +- [Diagnose VM network traffic filter problem — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/diagnose-vm-network-traffic-filtering-problem) +- [Network security groups overview — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network/network-security-groups-overview) diff --git a/plugin/skills/azure-network-watcher/references/next-hop.md b/plugin/skills/azure-network-watcher/references/next-hop.md new file mode 100644 index 000000000..2f2f016dc --- /dev/null +++ b/plugin/skills/azure-network-watcher/references/next-hop.md @@ -0,0 +1,236 @@ +# Next Hop + +Next hop is a Network Watcher diagnostic that determines the next hop type and IP address for traffic from a VM to a specified destination. It is the primary tool for diagnosing routing issues, verifying UDR (User-Defined Route) configurations, and identifying why traffic is being routed unexpectedly. + +## How It Works + +Next hop queries the effective routes for a VM's network interface and returns: + +1. **Next hop type** — the type of Azure resource or routing mechanism that handles the packet +2. **Next hop IP address** — the IP address of the next hop (if applicable) +3. **Route table ID** — the route table that contains the matching route + +Azure evaluates routes using longest prefix match. When multiple routes match a destination, the most specific route wins. Next hop shows you exactly which route is selected. + +## Required Parameters + +| Parameter | Description | Example | +|-----------|-------------|---------| +| `--vm` | Source VM name | `myVM` | +| `--resource-group` | Resource group of the VM | `myRG` | +| `--source-ip` | Source IP address (must be an IP on the VM's NIC) | `10.0.0.4` | +| `--dest-ip` | Destination IP address | `10.1.0.4` | + +## CLI Usage + +### Basic next hop check + +```bash +az network watcher show-next-hop \ + --resource-group myRG \ + --vm myVM \ + --source-ip 10.0.0.4 \ + --dest-ip 10.1.0.4 +``` + +**Example output:** +```json +{ + "nextHopIpAddress": "", + "nextHopType": "VnetPeering", + "routeTableId": "System Route" +} +``` + +### Check route to the internet + +```bash +az network watcher show-next-hop \ + --resource-group myRG \ + --vm myVM \ + --source-ip 10.0.0.4 \ + --dest-ip 8.8.8.8 +``` + +### Check route to an on-premises network + +```bash +az network watcher show-next-hop \ + --resource-group myRG \ + --vm myVM \ + --source-ip 10.0.0.4 \ + --dest-ip 192.168.1.10 +``` + +### Check route to a specific Azure service + +```bash +az network watcher show-next-hop \ + --resource-group myRG \ + --vm myVM \ + --source-ip 10.0.0.4 \ + --dest-ip 52.239.228.100 +``` + +## Next Hop Types + +| Next Hop Type | Description | +|---------------|-------------| +| `VirtualNetwork` | Destination is within the VNet's address space (including peered VNets with `VnetPeering`) | +| `VnetPeering` | Traffic routes via VNet peering to a peered virtual network | +| `VirtualNetworkGateway` | Traffic routes through a VPN Gateway or ExpressRoute Gateway | +| `Internet` | Traffic routes to the internet via Azure's default route | +| `VirtualAppliance` | Traffic routes to a Network Virtual Appliance (NVA) via a UDR — the next hop IP is the NVA's IP | +| `None` | Traffic is dropped (black-holed) — destination is unreachable | +| `VnetLocal` | Traffic stays within the same subnet | + +## Interpreting Results + +### Expected: Traffic routes within the VNet + +```json +{ + "nextHopIpAddress": "", + "nextHopType": "VirtualNetwork", + "routeTableId": "System Route" +} +``` + +Traffic uses the default system route to reach destinations within the VNet address space. + +### Expected: Traffic routes through an NVA + +```json +{ + "nextHopIpAddress": "10.0.2.4", + "nextHopType": "VirtualAppliance", + "routeTableId": "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/routeTables/myRouteTable" +} +``` + +A UDR in `myRouteTable` directs traffic to the NVA at `10.0.2.4`. + +### Unexpected: Traffic is black-holed + +```json +{ + "nextHopIpAddress": "", + "nextHopType": "None", + "routeTableId": "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/routeTables/myRouteTable" +} +``` + +A `None` next hop means the traffic is dropped. Common causes: +- A UDR pointing to a non-existent next hop IP +- A UDR with an address prefix that catches traffic unintentionally +- IP forwarding not enabled on the NVA's NIC + +### Unexpected: Traffic goes to the internet instead of a VPN + +```json +{ + "nextHopIpAddress": "", + "nextHopType": "Internet", + "routeTableId": "System Route" +} +``` + +If you expected traffic to route through a VPN gateway to an on-premises network, this means: +- BGP routes from the gateway are not propagating +- Route propagation is disabled on the subnet's route table +- The on-premises address prefix is not advertised via BGP + +## Diagnostic Scenarios + +### Scenario 1: VM cannot reach a peered VNet + +```bash +az network watcher show-next-hop \ + --resource-group myRG --vm vmA \ + --source-ip 10.0.0.4 --dest-ip 10.1.0.4 +``` + +If next hop type is `None`: the peering may be broken or the remote VNet address space changed. Check: +- Peering status on both sides (must be "Connected") +- Remote VNet address space includes the destination IP + +If next hop type is `Internet`: the destination IP is not recognized as part of any VNet. Verify the VNet address spaces. + +### Scenario 2: Traffic should route through a firewall NVA but doesn't + +```bash +az network watcher show-next-hop \ + --resource-group myRG --vm appVM \ + --source-ip 10.0.1.4 --dest-ip 8.8.8.8 +``` + +If next hop type is `Internet` instead of `VirtualAppliance`: +- The UDR `0.0.0.0/0 -> NVA IP` is not applied to the VM's subnet +- The route table is not associated with the subnet +- A more specific route is overriding the UDR + +Verify with: +```bash +# Show effective routes on the VM's NIC +az network nic show-effective-route-table \ + --resource-group myRG \ + --name myVM-nic \ + --output table +``` + +### Scenario 3: Asymmetric routing suspected + +Test next hop from both VMs: + +```bash +# Forward path: VM-A to VM-B +az network watcher show-next-hop \ + --resource-group myRG --vm vmA \ + --source-ip 10.0.0.4 --dest-ip 10.1.0.4 + +# Return path: VM-B to VM-A +az network watcher show-next-hop \ + --resource-group myRG --vm vmB \ + --source-ip 10.1.0.4 --dest-ip 10.0.0.4 +``` + +If the forward path routes through an NVA but the return path does not, you have asymmetric routing. The NVA may drop the return traffic because it did not see the original flow. + +### Scenario 4: Verifying route table correctness + +After creating or modifying a UDR, verify it takes effect: + +```bash +# Expected: traffic to 10.2.0.0/16 should go through NVA at 10.0.2.4 +az network watcher show-next-hop \ + --resource-group myRG --vm myVM \ + --source-ip 10.0.0.4 --dest-ip 10.2.0.10 +``` + +If the result shows the NVA IP as the next hop, the UDR is working. If not, check: +- Route table is associated with the correct subnet +- UDR address prefix matches the destination +- No higher-priority route is overriding it + +## Effective Routes vs Next Hop + +| Tool | Use When | +|------|----------| +| **Next hop** | Quick answer: where does traffic to a specific destination go? | +| **Effective routes** (`az network nic show-effective-route-table`) | Full view of all routes on a NIC, for comprehensive route table analysis | + +Next hop queries the effective routes under the hood but returns only the winning route for a single destination. + +## Limitations + +- Source IP must be assigned to the VM's NIC +- Only evaluates the source VM's routing — does not test the return path +- Does not evaluate NSG rules (use IP flow verify for that) +- Cannot test from non-VM resources (e.g., Azure Firewall, Application Gateway) +- The VM must be in a running state + +## Learn More + +- [Next hop overview — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/next-hop-overview) +- [Diagnose VM routing problems — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/diagnose-vm-network-routing-problem) +- [Virtual network traffic routing — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network/virtual-networks-udr-overview) diff --git a/plugin/skills/azure-network-watcher/references/nsg-flow-logs.md b/plugin/skills/azure-network-watcher/references/nsg-flow-logs.md new file mode 100644 index 000000000..ada8c96d8 --- /dev/null +++ b/plugin/skills/azure-network-watcher/references/nsg-flow-logs.md @@ -0,0 +1,248 @@ +# NSG Flow Logs + +NSG flow logs record information about IP traffic flowing through network security groups. They are essential for network monitoring, security auditing, usage tracking, and compliance. Version 2 flow logs add byte and packet counts, which are required for Traffic Analytics. + +## Flow Log Versions + +| Feature | Version 1 | Version 2 | +|---------|-----------|-----------| +| Flow state tracking | Yes | Yes | +| Bytes per flow | No | Yes | +| Packets per flow | No | Yes | +| Traffic Analytics support | Limited | Full | +| Throughput information | No | Yes | + +Always use version 2 for new deployments. + +## Enabling NSG Flow Logs + +### Basic flow log (v2) + +```bash +az network watcher flow-log create \ + --resource-group myRG \ + --name myFlowLog \ + --nsg myNSG \ + --storage-account myStorageAccount \ + --enabled true \ + --format JSON \ + --log-version 2 \ + --retention 90 +``` + +### Flow log with Traffic Analytics + +```bash +az network watcher flow-log create \ + --resource-group myRG \ + --name myFlowLog \ + --nsg myNSG \ + --storage-account myStorageAccount \ + --enabled true \ + --format JSON \ + --log-version 2 \ + --retention 90 \ + --traffic-analytics true \ + --workspace myLogAnalyticsWorkspace \ + --interval 10 +``` + +The `--interval` parameter sets the Traffic Analytics processing interval in minutes (10 or 60). + +### VNet flow logs (newer alternative to NSG flow logs) + +Azure now also supports VNet flow logs, which log at the virtual network level: + +```bash +az network watcher flow-log create \ + --resource-group myRG \ + --name myVNetFlowLog \ + --vnet myVNet \ + --storage-account myStorageAccount \ + --enabled true \ + --format JSON \ + --log-version 2 \ + --retention 90 \ + --traffic-analytics true \ + --workspace myLogAnalyticsWorkspace +``` + +VNet flow logs capture all traffic in the VNet, not just traffic evaluated by NSGs. + +## Log Analytics Workspace Integration + +Traffic Analytics requires a Log Analytics workspace. The workspace processes raw flow log data and stores it in the `AzureNetworkAnalytics_CL` table. + +```bash +# Create a workspace if needed +az monitor log-analytics workspace create \ + --resource-group myRG \ + --workspace-name myWorkspace \ + --location eastus + +# Update a flow log to enable Traffic Analytics +az network watcher flow-log update \ + --resource-group myRG \ + --name myFlowLog \ + --traffic-analytics true \ + --workspace myWorkspace \ + --interval 10 +``` + +## Retention + +| Storage Location | Retention Configuration | +|------------------|------------------------| +| Storage account | `--retention` parameter (1–365 days, 0 = forever) | +| Log Analytics workspace | Workspace-level retention settings (30–730 days) | + +Storage account retention uses Azure Storage lifecycle management to automatically delete old flow log files. + +## Flow Log Schema (v2) + +Flow logs are stored as JSON files in the storage account at: +``` +https://{account}.blob.core.windows.net/insights-logs-networksecuritygroupflowevent/resourceId=/SUBSCRIPTIONS/{sub}/RESOURCEGROUPS/{rg}/PROVIDERS/MICROSOFT.NETWORK/NETWORKSECURITYGROUPS/{nsg}/y={year}/m={month}/d={day}/h={hour}/m=00/macAddress={mac}/PT1H.json +``` + +### Record structure + +Each flow tuple in version 2 contains: +``` +{timestamp},{source IP},{dest IP},{source port},{dest port},{protocol},{traffic flow},{traffic decision},{flow state},{packets source to dest},{bytes source to dest},{packets dest to source},{bytes dest to source} +``` + +| Field | Values | +|-------|--------| +| Protocol | T (TCP), U (UDP) | +| Traffic flow | I (inbound), O (outbound) | +| Traffic decision | A (allowed), D (denied) | +| Flow state | B (begin), C (continuing), E (end) | + +### Example flow tuple (v2) + +``` +1636479326,10.0.0.4,10.1.0.4,49152,443,T,I,A,B,12,1440,8,960 +``` + +This means: at timestamp 1636479326, TCP traffic from 10.0.0.4:49152 to 10.1.0.4:443 was inbound, allowed, and this is the begin of the flow. 12 packets (1440 bytes) were sent source-to-dest, and 8 packets (960 bytes) were sent dest-to-source. + +## Common Log Analytics Queries + +### Top 10 talking IP pairs + +```kusto +AzureNetworkAnalytics_CL +| where SubType_s == "FlowLog" +| where FlowType_s == "IntraVNet" or FlowType_s == "InterVNet" +| summarize TotalBytes = sum(InboundBytes_d + OutboundBytes_d) by SrcIP_s, DestIP_s +| top 10 by TotalBytes desc +``` + +### Denied flows by NSG rule + +```kusto +AzureNetworkAnalytics_CL +| where SubType_s == "FlowLog" +| where FlowStatus_s == "D" +| summarize Count = count() by NSGRule_s, SrcIP_s, DestPort_d +| order by Count desc +| take 20 +``` + +### Traffic volume over time + +```kusto +AzureNetworkAnalytics_CL +| where SubType_s == "FlowLog" +| summarize TotalMB = sum(InboundBytes_d + OutboundBytes_d) / 1048576 by bin(TimeGenerated, 1h) +| render timechart +``` + +### Flows from a specific source IP + +```kusto +AzureNetworkAnalytics_CL +| where SubType_s == "FlowLog" +| where SrcIP_s == "10.0.0.4" +| project TimeGenerated, SrcIP_s, DestIP_s, DestPort_d, L7Protocol_s, FlowStatus_s, NSGRule_s +| order by TimeGenerated desc +| take 100 +``` + +### Identify allowed traffic to sensitive ports + +```kusto +AzureNetworkAnalytics_CL +| where SubType_s == "FlowLog" +| where FlowStatus_s == "A" +| where DestPort_d in (22, 3389, 1433, 3306, 5432) +| summarize Count = count() by DestPort_d, SrcIP_s, DestIP_s +| order by Count desc +``` + +### Cross-region traffic (cost analysis) + +```kusto +AzureNetworkAnalytics_CL +| where SubType_s == "FlowLog" +| where FlowType_s == "InterVNet" +| where SrcRegion_s != DestRegion_s +| summarize CrossRegionGB = sum(InboundBytes_d + OutboundBytes_d) / 1073741824 by SrcRegion_s, DestRegion_s +| order by CrossRegionGB desc +``` + +## Managing Flow Logs + +```bash +# List all flow logs in a region +az network watcher flow-log list --location eastus --output table + +# Show a specific flow log +az network watcher flow-log show --location eastus --name myFlowLog + +# Update flow log settings +az network watcher flow-log update \ + --resource-group myRG \ + --name myFlowLog \ + --retention 180 + +# Disable a flow log +az network watcher flow-log update \ + --resource-group myRG \ + --name myFlowLog \ + --enabled false + +# Delete a flow log +az network watcher flow-log delete --location eastus --name myFlowLog +``` + +## Traffic Analytics Insights + +Traffic Analytics processes flow logs and provides: + +- **Flow visualization** — geographic map of traffic flows +- **Top talkers** — VMs and IPs generating the most traffic +- **Security insights** — open ports, flows from malicious IPs, NSG rule hit counts +- **Bandwidth utilization** — VNet and subnet throughput trends +- **Geo-mapping** — origin countries/regions for internet-bound traffic + +Access Traffic Analytics in the Azure portal under **Network Watcher > Traffic Analytics**. + +## Cost Considerations + +| Component | Cost Factor | +|-----------|-------------| +| NSG flow logs | Per GB of flow log data collected | +| Storage account | Standard blob storage rates for flow log files | +| Log Analytics workspace | Per GB of data ingested (when Traffic Analytics is enabled) | +| Traffic Analytics | Per GB of flow log data processed | + +Use retention policies and processing intervals to manage costs. A 60-minute processing interval is cheaper than 10-minute. + +## Learn More + +- [NSG flow logs overview — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/nsg-flow-logs-overview) +- [Traffic Analytics — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/traffic-analytics) +- [VNet flow logs — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/vnet-flow-logs-overview) +- [Log Analytics query examples — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/traffic-analytics-schema) diff --git a/plugin/skills/azure-network-watcher/references/packet-capture.md b/plugin/skills/azure-network-watcher/references/packet-capture.md new file mode 100644 index 000000000..5340b9626 --- /dev/null +++ b/plugin/skills/azure-network-watcher/references/packet-capture.md @@ -0,0 +1,205 @@ +# Packet Capture + +Packet capture in Network Watcher records network traffic to and from a VM for analysis. It is an essential diagnostic tool for troubleshooting connectivity issues, analyzing application behavior, and investigating security incidents. + +## Prerequisites + +Before creating a packet capture, the target VM must have the Network Watcher VM extension installed: + +```bash +# Install on a Linux VM +az vm extension set \ + --resource-group myRG \ + --vm-name myLinuxVM \ + --name NetworkWatcherAgentLinux \ + --publisher Microsoft.Azure.NetworkWatcher + +# Install on a Windows VM +az vm extension set \ + --resource-group myRG \ + --vm-name myWindowsVM \ + --name NetworkWatcherAgentWindows \ + --publisher Microsoft.Azure.NetworkWatcher + +# Verify the extension is installed +az vm extension show \ + --resource-group myRG \ + --vm-name myVM \ + --name NetworkWatcherAgentLinux +``` + +The VM agent must also be in a healthy running state. + +## Creating a Packet Capture + +### Basic capture (all traffic, stored in storage account) + +```bash +az network watcher packet-capture create \ + --resource-group myRG \ + --vm myVM \ + --name myCapture \ + --storage-account myStorageAccount \ + --time-limit 300 +``` + +### Capture with filters + +Filters reduce the capture size by recording only matching traffic. Multiple filters use OR logic — traffic matching any filter is captured. + +```bash +az network watcher packet-capture create \ + --resource-group myRG \ + --vm myVM \ + --name filteredCapture \ + --storage-account myStorageAccount \ + --time-limit 600 \ + --filters '[ + {"protocol":"TCP","localIPAddress":"10.0.0.4","remoteIPAddress":"10.1.0.0/24","localPort":"443"}, + {"protocol":"TCP","localIPAddress":"10.0.0.4","remotePort":"3306"} + ]' +``` + +### Filter parameters + +| Parameter | Description | Example | +|-----------|-------------|---------| +| `protocol` | TCP, UDP, or Any | `TCP` | +| `localIPAddress` | VM's IP address or CIDR range | `10.0.0.4` or `10.0.0.0/24` | +| `remoteIPAddress` | Remote IP address or CIDR range | `10.1.0.4` or `0.0.0.0/0` | +| `localPort` | Local port or range | `443` or `80-443` | +| `remotePort` | Remote port or range | `3306` or `1024-65535` | + +## Storage Options + +Packet captures can be stored in three ways: + +### 1. Storage account only + +```bash +az network watcher packet-capture create \ + --resource-group myRG \ + --vm myVM \ + --name storageCapture \ + --storage-account myStorageAccount \ + --storage-path "https://mystorage.blob.core.windows.net/captures" \ + --time-limit 300 +``` + +The capture file is stored as a `.cap` file in the storage account under the path `https://{account}.blob.core.windows.net/network-watcher-logs/subscriptions/{sub}/resourcegroups/{rg}/providers/microsoft.compute/virtualmachines/{vm}/{capture-name}/{timestamp}.cap`. + +### 2. Local file on the VM + +```bash +az network watcher packet-capture create \ + --resource-group myRG \ + --vm myVM \ + --name localCapture \ + --file-path "/var/captures/mycapture.cap" \ + --time-limit 300 +``` + +On Windows VMs, use a path like `C:\captures\mycapture.cap`. The file is stored directly on the VM's disk. + +### 3. Both storage account and local file + +```bash +az network watcher packet-capture create \ + --resource-group myRG \ + --vm myVM \ + --name dualCapture \ + --storage-account myStorageAccount \ + --file-path "/var/captures/mycapture.cap" \ + --time-limit 300 +``` + +## Capture Limits + +| Setting | Default | Maximum | +|---------|---------|---------| +| `--time-limit` | 18,000 seconds (5 hours) | 18,000 seconds | +| `--bytes-to-capture-per-packet` | 0 (entire packet) | 4,294,967,295 bytes | +| `--total-bytes-per-session` | 1,073,741,824 (1 GB) | 4,294,967,295 bytes | +| Concurrent captures per region | — | 10 per VM, 100 per subscription | + +## Managing Captures + +```bash +# List all packet captures in a region +az network watcher packet-capture list --location eastus --output table + +# Check capture status +az network watcher packet-capture show-status --location eastus --name myCapture + +# Stop a running capture +az network watcher packet-capture stop --location eastus --name myCapture + +# Delete a capture session (does not delete the capture file) +az network watcher packet-capture delete --location eastus --name myCapture +``` + +## Capture Status Values + +| Status | Description | +|--------|-------------| +| `Running` | Capture is actively recording traffic | +| `Stopped` | Capture was manually stopped or timed out | +| `Failed` | Capture encountered an error (check the error reason) | +| `NotStarted` | Capture was created but has not started | + +## Analyzing Captures + +### Download from storage account + +```bash +# List blobs in the network-watcher-logs container +az storage blob list \ + --account-name myStorageAccount \ + --container-name network-watcher-logs \ + --output table + +# Download the capture file +az storage blob download \ + --account-name myStorageAccount \ + --container-name network-watcher-logs \ + --name "subscriptions/{sub}/resourcegroups/myRG/providers/microsoft.compute/virtualmachines/myVM/myCapture/{timestamp}.cap" \ + --file mycapture.cap +``` + +### Analysis tools + +Capture files are in standard `.cap` (pcap) format and can be analyzed with: + +- **Wireshark** — full-featured GUI packet analyzer (most common) +- **tcpdump** — command-line analysis on Linux +- **Microsoft Message Analyzer** — Windows-based analysis (deprecated but still functional) +- **tshark** — Wireshark's command-line companion + +### Common Wireshark display filters for Azure captures + +| Filter | Purpose | +|--------|---------| +| `tcp.port == 443` | HTTPS traffic | +| `tcp.flags.syn == 1 && tcp.flags.ack == 0` | TCP connection initiations (SYN only) | +| `tcp.analysis.retransmission` | Retransmitted packets (indicates network issues) | +| `dns` | DNS queries and responses | +| `ip.addr == 10.0.0.4` | Traffic to/from a specific IP | +| `tcp.flags.reset == 1` | TCP RST packets (connection rejections) | +| `http.response.code >= 400` | HTTP error responses | + +## Troubleshooting Common Issues + +| Issue | Cause | Resolution | +|-------|-------|------------| +| Capture fails to start | Extension not installed | Install the Network Watcher VM extension | +| Capture fails to start | VM agent unhealthy | Restart the VM agent or the VM | +| Empty capture file | No traffic matching filters | Broaden filters or remove them | +| Capture stops early | Total bytes limit reached | Increase `--total-bytes-per-session` or use filters | +| Cannot access capture file | Storage account permissions | Verify the Network Watcher has access to the storage account | +| Extension install fails | VM not running | Start the VM before installing the extension | + +## Learn More + +- [Packet capture overview — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/packet-capture-overview) +- [Create a packet capture — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/packet-capture-vm-portal) +- [Analyze packet captures with Wireshark — Microsoft Learn](https://learn.microsoft.com/azure/network-watcher/packet-capture-inspect) diff --git a/plugin/skills/azure-private-link/SKILL.md b/plugin/skills/azure-private-link/SKILL.md new file mode 100644 index 000000000..4a42397b7 --- /dev/null +++ b/plugin/skills/azure-private-link/SKILL.md @@ -0,0 +1,118 @@ +--- +name: azure-private-link +description: "Configure Azure Private Link and private endpoints to securely access PaaS services and custom services over a private network connection. WHEN: private endpoint, private link, private access, disable public access, PaaS private connectivity. DO NOT USE FOR: VNet peering or VNet configuration (use azure-virtual-network), VPN tunnels (use azure-vpn-gateway), DNS zone management only (use azure-dns)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Private Link Skill + +## When to Use This Skill + +- User wants to connect to Azure PaaS services (Storage, SQL, Key Vault, etc.) over a private IP +- User needs to disable public access to a PaaS service and use private connectivity only +- User wants to create a Private Link Service to expose their own application privately +- User asks about private endpoint DNS configuration +- User needs to troubleshoot private endpoint connectivity or DNS resolution +- User wants to understand auto-approval vs manual approval for private endpoints +- User is designing a zero-trust network with no public PaaS endpoints + +## Rules + +1. Always configure DNS correctly — private endpoints are useless without proper DNS resolution to the private IP. +2. Recommend Azure Private DNS zones for automatic DNS registration of private endpoints. +3. When disabling public access on a PaaS service, verify that ALL consumers have private endpoint access first. +4. Private endpoints consume an IP address in the subnet — plan subnet sizing accordingly. +5. Private endpoint and the target resource can be in different subscriptions, regions, or even tenants. +6. For hybrid scenarios (on-premises access), configure DNS conditional forwarding to Azure Private DNS Resolver or Azure DNS (168.63.129.16). +7. A single PaaS resource can have multiple private endpoints in different VNets. +8. Private endpoints support NSG enforcement — recommend enabling it for zero-trust architectures. +9. Always remind users that creating a private endpoint does NOT automatically disable public access — that must be done separately. +10. Use sub-resources (groupId) to target specific features (e.g., "blob" vs "file" for Storage accounts). + +## MCP Tools + +| Tool | Command | Purpose | +|------|---------|---------| +| `azure__network` | `private_endpoint_list` | List all private endpoints in a subscription or resource group | +| `azure__network` | `private_endpoint_get` | Get details of a specific private endpoint including connection status | + +## CLI Fallback + +```bash +# Create a private endpoint for Azure Storage (blob) +az network private-endpoint create -g MyRG -n MyStoragePE \ + --vnet-name MyVNet --subnet PrivateEndpointSubnet \ + --private-connection-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{sa} \ + --group-id blob \ + --connection-name MyStorageConnection + +# List private endpoints +az network private-endpoint list -g MyRG -o table + +# Show private endpoint details +az network private-endpoint show -g MyRG -n MyStoragePE + +# Check private endpoint connection status +az network private-endpoint show -g MyRG -n MyStoragePE --query 'privateLinkServiceConnections[0].privateLinkServiceConnectionState' + +# Create a Private DNS zone for blob storage +az network private-dns zone create -g MyRG -n privatelink.blob.core.windows.net + +# Link Private DNS zone to VNet +az network private-dns link vnet create -g MyRG --zone-name privatelink.blob.core.windows.net \ + -n MyVNetLink --virtual-network MyVNet --registration-enabled false + +# Create DNS zone group for automatic DNS registration +az network private-endpoint dns-zone-group create -g MyRG --endpoint-name MyStoragePE \ + -n default --private-dns-zone /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net \ + --zone-name blob + +# Create a Private Link Service +az network private-link-service create -g MyRG -n MyPLS \ + --vnet-name MyVNet --subnet PLSSubnet \ + --lb-name MyInternalLB --lb-frontend-ip-configs MyFrontend \ + --private-ip-address 10.0.3.5 + +# Manage private endpoint connections on a resource +az network private-endpoint-connection approve --resource-name MyStorage -g MyRG \ + --type Microsoft.Storage/storageAccounts -n MyConnectionName +az network private-endpoint-connection reject --resource-name MyStorage -g MyRG \ + --type Microsoft.Storage/storageAccounts -n MyConnectionName +``` + +## Key Concepts + +### Private Endpoint DNS Zone Names (Common Services) + +| Azure Service | Sub-Resource | Private DNS Zone Name | +|--------------|-------------|----------------------| +| Storage — Blob | blob | `privatelink.blob.core.windows.net` | +| Storage — File | file | `privatelink.file.core.windows.net` | +| Storage — Table | table | `privatelink.table.core.windows.net` | +| Storage — Queue | queue | `privatelink.queue.core.windows.net` | +| SQL Database | sqlServer | `privatelink.database.windows.net` | +| Cosmos DB — SQL | Sql | `privatelink.documents.azure.com` | +| Key Vault | vault | `privatelink.vaultcore.azure.net` | +| Azure Monitor | azuremonitor | `privatelink.monitor.azure.com` | +| App Service | sites | `privatelink.azurewebsites.net` | +| ACR | registry | `privatelink.azurecr.io` | +| Event Hub | namespace | `privatelink.servicebus.windows.net` | +| Service Bus | namespace | `privatelink.servicebus.windows.net` | + +### Connection States + +| State | Meaning | Action Needed | +|-------|---------|---------------| +| Pending | Connection awaits approval | Approve or reject on the resource side | +| Approved | Connection is active | None — traffic flows | +| Rejected | Connection was rejected | Delete PE and recreate if needed | +| Disconnected | Connection was removed by resource owner | Delete PE | + +## References + +- [Private Endpoint DNS Configuration](references/private-endpoint-dns.md) +- [Private Link Service Guide](references/private-link-service.md) +- [Approval Workflow](references/approval-workflow.md) diff --git a/plugin/skills/azure-private-link/references/approval-workflow.md b/plugin/skills/azure-private-link/references/approval-workflow.md new file mode 100644 index 000000000..8b319bddb --- /dev/null +++ b/plugin/skills/azure-private-link/references/approval-workflow.md @@ -0,0 +1,278 @@ +# Private Endpoint Approval Workflow + +When a consumer creates a private endpoint targeting a resource they don't own, the connection may require approval from the resource owner. Understanding the approval workflow is essential for both service providers and consumers. + +## Auto-Approval vs Manual Approval + +### Auto-Approval + +Auto-approval occurs when the private endpoint creator has sufficient RBAC permissions on the target resource. In these scenarios, the connection is **immediately approved** without any manual intervention: + +- **Same-subscription PaaS resources** — if you have `Microsoft.Network/privateEndpoints/write` and the appropriate role (e.g., Contributor, Owner) on the target PaaS resource, the connection auto-approves. +- **Private Link Service with auto-approval list** — if the consumer's subscription ID is in the PLS auto-approval list, the connection auto-approves. + +### Manual Approval + +Manual approval is required when: + +- The consumer lacks direct RBAC permissions on the target resource (e.g., cross-subscription or cross-tenant connections). +- The Private Link Service provider has not added the consumer's subscription to the auto-approval list. +- The consumer explicitly creates a manual connection (using `--manual-request` flag). + +In manual approval mode, the connection enters **Pending** state and remains there until the resource owner approves or rejects it. + +## Connection Lifecycle + +``` +Consumer creates Resource owner Resource owner Resource owner +Private Endpoint approves rejects removes + │ │ │ │ + ▼ ▼ ▼ ▼ + ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ + │ Pending │───────► │ Approved │ │ Rejected │ │ Disconnected │ + └─────────┘ └──────────┘ └──────────┘ └──────────────┘ + │ │ ▲ + │ │─────────────────────────────────────────────┘ + │ Resource owner removes connection + │ + └──────► Consumer deletes PE (connection disappears) +``` + +| State | Description | Consumer Action | Provider Action | +|-------|-------------|-----------------|-----------------| +| **Pending** | Consumer created PE; awaiting approval | Wait or contact resource owner | Approve or reject | +| **Approved** | Connection is active; traffic flows | Use the service | Monitor; can disconnect later | +| **Rejected** | Resource owner denied the request | Delete PE; request approval through other channels | Can re-approve if needed | +| **Disconnected** | Resource owner removed an approved connection | Delete PE and recreate if access is still needed | Re-approve if reconnection needed | + +## Configuring Auto-Approval by Subscription ID + +For Private Link Service providers who want to auto-approve connections from trusted subscriptions: + +```bash +# Set auto-approval for specific consumer subscriptions +az network private-link-service update \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --auto-approval \ + /subscriptions/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb \ + /subscriptions/cccccccc-4444-5555-6666-dddddddddddd +``` + +For PaaS resources (Storage, SQL, Key Vault, etc.), auto-approval is controlled by RBAC. If the PE creator has sufficient permissions on the target resource, the connection auto-approves. + +## Visibility Settings + +Visibility controls who can **discover** and **request connections** to your Private Link Service: + +```bash +# Only allow specific subscriptions to see the PLS +az network private-link-service update \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --visibility \ + /subscriptions/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb \ + /subscriptions/cccccccc-4444-5555-6666-dddddddddddd + +# Allow all subscriptions to see the PLS +az network private-link-service update \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --visibility '*' +``` + +> **Note:** Visibility and auto-approval are independent settings. A subscription can be in the visibility list (can request a connection) but not in auto-approval (connection requires manual approval). + +## Managing Pending Connections + +### List All Connections on a PaaS Resource + +```bash +# For a Storage account +az network private-endpoint-connection list \ + --resource-name MyStorageAccount \ + -g MyRG \ + --type Microsoft.Storage/storageAccounts \ + -o table +``` + +### List All Connections on a Private Link Service + +```bash +az network private-link-service show \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --query "privateEndpointConnections[].[name, privateLinkServiceConnectionState.status, privateLinkServiceConnectionState.description]" \ + -o table +``` + +### Approve a Pending Connection + +```bash +# On a PaaS resource (e.g., Storage) +az network private-endpoint-connection approve \ + --resource-name MyStorageAccount \ + -g MyRG \ + --type Microsoft.Storage/storageAccounts \ + -n MyConnectionName \ + --description "Approved for project Alpha" + +# On a Private Link Service +az network private-endpoint-connection approve \ + --resource-name MyPrivateLinkService \ + -g ProviderRG \ + --type Microsoft.Network/privateLinkServices \ + -n MyConnectionName \ + --description "Approved by networking team" +``` + +### Reject a Connection + +```bash +az network private-endpoint-connection reject \ + --resource-name MyStorageAccount \ + -g MyRG \ + --type Microsoft.Storage/storageAccounts \ + -n MyConnectionName \ + --description "Rejected: use the approved private endpoint in the hub VNet instead" +``` + +### Remove (Delete) a Connection + +```bash +az network private-endpoint-connection delete \ + --resource-name MyStorageAccount \ + -g MyRG \ + --type Microsoft.Storage/storageAccounts \ + -n MyConnectionName +``` + +## Cross-Tenant Private Endpoint Connections + +Private endpoints can connect to resources in a different Azure AD tenant. This is common in: + +- **ISV/SaaS scenarios** — a vendor exposes a Private Link Service, and customers in their own tenants connect to it. +- **Multi-tenant enterprise** — business units in separate tenants need private access to shared services. + +### How Cross-Tenant Works + +1. **Provider** shares the PLS alias (e.g., `MyPLS.{guid}.{region}.azure.privatelinkservice`) with the consumer. +2. **Consumer** creates a private endpoint using the alias. The connection goes to **Pending** state. +3. **Provider** approves the connection on the PLS. +4. **Consumer** configures DNS in their VNet to resolve the service hostname to the private endpoint IP. + +Cross-tenant connections always require manual approval (auto-approval only works within the same tenant or by subscription ID in the auto-approval list). + +```bash +# Consumer (different tenant) creates PE using alias +az network private-endpoint create \ + -g ConsumerRG \ + -n CrossTenantPE \ + --vnet-name ConsumerVNet \ + --subnet ConsumerSubnet \ + --manual-request true \ + --private-connection-resource-id "" \ + --manual-request-connection-id MyPLS.abcdef12-3456-7890.eastus.azure.privatelinkservice \ + --connection-name CrossTenantConnection \ + --request-message "Consumer Corp requesting access - ticket INC0012345" +``` + +## Azure Policy for Enforcing Private Endpoints + +Use Azure Policy to ensure that PaaS resources are only accessible through private endpoints: + +### Common Built-in Policies + +| Policy | Effect | Description | +|--------|--------|-------------| +| `Configure {service} to use private DNS zones` | DeployIfNotExists | Auto-creates DNS zone groups when private endpoints are created | +| `{service} should use private link` | Audit / Deny | Audits or blocks PaaS resources without private endpoints | +| `{service} should disable public network access` | Audit / Deny | Ensures public access is disabled | + +### Example: Deny Storage Accounts Without Private Endpoints + +```json +{ + "if": { + "allOf": [ + { + "field": "type", + "equals": "Microsoft.Storage/storageAccounts" + }, + { + "count": { + "field": "Microsoft.Storage/storageAccounts/privateEndpointConnections[*]", + "where": { + "field": "Microsoft.Storage/storageAccounts/privateEndpointConnections[*].privateLinkServiceConnectionState.status", + "equals": "Approved" + } + }, + "less": 1 + } + ] + }, + "then": { + "effect": "Audit" + } +} +``` + +### Example: Auto-Configure DNS Zone Groups (DeployIfNotExists) + +Azure provides built-in policies under the initiative **"Configure Azure PaaS services to use private DNS zones"** that automatically create DNS zone groups when private endpoints are created. Assign this initiative at the management group or subscription level to ensure consistent DNS configuration. + +```bash +# Assign the built-in initiative +az policy assignment create \ + --name "EnforcePEDnsZones" \ + --display-name "Auto-configure Private Endpoint DNS zones" \ + --policy-set-definition "e8e3286c-a037-4148-8802-5ff4e3e0390b" \ + --scope /subscriptions/{sub-id} \ + --params '{"privateDnsZoneId": {"value": "/subscriptions/{sub}/resourceGroups/MyDnsRG/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net"}}' +``` + +## Best Practices: When to Use Auto-Approval vs Manual + +| Scenario | Recommendation | Reason | +|----------|---------------|--------| +| Internal team in the same subscription | Auto-approval (RBAC) | Minimal friction; RBAC already controls access | +| Trusted partner subscriptions | Auto-approval list | Streamlines onboarding for known consumers | +| External customers / ISV model | Manual approval | Verify identity and authorization before granting access | +| Cross-tenant connections | Manual approval (required) | Auto-approval doesn't work cross-tenant | +| Marketplace / public offering | Manual or auto with visibility controls | Use visibility to restrict who can discover the service | +| Highly regulated environments | Manual approval with audit trail | Ensures every connection is explicitly reviewed and documented | + +## Auditing: Tracking Who Approved or Rejected Connections + +### Activity Log + +Every approve, reject, and delete action on private endpoint connections is recorded in the Azure Activity Log: + +```bash +# Query activity log for private endpoint connection events +az monitor activity-log list \ + --resource-group MyRG \ + --query "[?contains(operationName.value, 'privateEndpointConnection')].{Operation:operationName.value, Status:status.value, Caller:caller, Time:eventTimestamp}" \ + --start-time 2024-01-01 \ + -o table +``` + +### Diagnostic Settings + +For long-term audit retention, configure Diagnostic Settings on the PaaS resource to send Activity Logs to: + +- **Log Analytics workspace** — for querying with KQL +- **Storage Account** — for compliance archival +- **Event Hub** — for SIEM integration + +### KQL Query for Private Endpoint Connection Approvals + +```kql +AzureActivity +| where OperationNameValue contains "privateEndpointConnection" +| where ActivityStatusValue == "Success" +| project TimeGenerated, Caller, OperationNameValue, ResourceGroup, Resource +| order by TimeGenerated desc +``` + +This query shows who approved, rejected, or removed private endpoint connections, including the timestamp and the resource involved. diff --git a/plugin/skills/azure-private-link/references/private-endpoint-dns.md b/plugin/skills/azure-private-link/references/private-endpoint-dns.md new file mode 100644 index 000000000..9bf890caa --- /dev/null +++ b/plugin/skills/azure-private-link/references/private-endpoint-dns.md @@ -0,0 +1,302 @@ +# Private Endpoint DNS Configuration + +DNS is the most critical — and most frequently misconfigured — aspect of Azure Private Endpoints. A private endpoint assigns a private IP address from your VNet subnet to a PaaS resource, but unless DNS resolves the service's FQDN to that private IP, clients will continue reaching the public endpoint or fail entirely. + +## Why DNS Is Critical: The CNAME Chain + +When you create a private endpoint, Azure inserts a CNAME record into the public DNS hierarchy for the service. This CNAME chain is what redirects resolution from the public zone to the privatelink zone: + +``` +mystorageaccount.blob.core.windows.net + → CNAME: mystorageaccount.privatelink.blob.core.windows.net + → A record: 10.0.1.5 (from Azure Private DNS zone) +``` + +**Without a Private DNS zone (or custom DNS entry):** + +``` +mystorageaccount.blob.core.windows.net + → CNAME: mystorageaccount.privatelink.blob.core.windows.net + → A record: (public IP from Azure — the CNAME falls through to public resolution) +``` + +The CNAME to `privatelink.*` is always created in public DNS when a private endpoint exists. The difference is whether a Private DNS zone provides the final A record pointing to the private IP. If no Private DNS zone is linked to the VNet, the query resolves to the public IP, and the private endpoint is bypassed. + +## Recommended Pattern: Azure Private DNS Zone Integration + +The recommended approach is to create an Azure Private DNS zone matching the privatelink zone name for the service, link it to every VNet that needs private access, and use DNS zone groups for automatic A record management. + +### Step-by-Step Setup + +**1. Create the Private DNS zone:** + +```bash +az network private-dns zone create \ + -g MyDnsRG \ + -n privatelink.blob.core.windows.net +``` + +**2. Link the zone to each VNet that should resolve private endpoint IPs:** + +```bash +az network private-dns link vnet create \ + -g MyDnsRG \ + --zone-name privatelink.blob.core.windows.net \ + -n link-to-hub-vnet \ + --virtual-network /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/HubVNet \ + --registration-enabled false +``` + +> **Important:** Set `--registration-enabled false`. Auto-registration is for VM DNS records, not private endpoints. Enabling it on a privatelink zone causes confusion and is unsupported. + +**3. Create a DNS zone group on the private endpoint for automatic A record management:** + +```bash +az network private-endpoint dns-zone-group create \ + -g MyRG \ + --endpoint-name MyStoragePE \ + -n default \ + --private-dns-zone /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net \ + --zone-name blob +``` + +The DNS zone group automatically creates an A record in the Private DNS zone when the private endpoint is created and removes it when the endpoint is deleted. This eliminates manual record management. + +## DNS Zone Group Details + +A DNS zone group is a resource attached to a private endpoint that automates A record lifecycle: + +- **On private endpoint creation** — the zone group creates A records in the linked Private DNS zone +- **On private endpoint deletion** — the zone group removes the A records +- **On IP change** (rare, e.g., if the PE is recreated) — the A record is updated + +Without a DNS zone group, you must manually create and maintain A records: + +```bash +# Manual A record creation (NOT recommended — use zone groups instead) +az network private-dns record-set a add-record \ + -g MyDnsRG \ + --zone-name privatelink.blob.core.windows.net \ + -n mystorageaccount \ + -a 10.0.1.5 +``` + +## Comprehensive Private DNS Zone Names by Azure Service + +| Azure Service | Sub-Resource (groupId) | Private DNS Zone Name | +|--------------|------------------------|----------------------| +| **Storage — Blob** | blob | `privatelink.blob.core.windows.net` | +| **Storage — Blob (secondary)** | blob_secondary | `privatelink.blob.core.windows.net` | +| **Storage — Table** | table | `privatelink.table.core.windows.net` | +| **Storage — Table (secondary)** | table_secondary | `privatelink.table.core.windows.net` | +| **Storage — Queue** | queue | `privatelink.queue.core.windows.net` | +| **Storage — Queue (secondary)** | queue_secondary | `privatelink.queue.core.windows.net` | +| **Storage — File** | file | `privatelink.file.core.windows.net` | +| **Storage — Web** | web | `privatelink.web.core.windows.net` | +| **Storage — Web (secondary)** | web_secondary | `privatelink.web.core.windows.net` | +| **Storage — DFS** | dfs | `privatelink.dfs.core.windows.net` | +| **Storage — DFS (secondary)** | dfs_secondary | `privatelink.dfs.core.windows.net` | +| **SQL Database** | sqlServer | `privatelink.database.windows.net` | +| **SQL Managed Instance** | managedInstance | `privatelink.{dnsPrefix}.database.windows.net` | +| **Cosmos DB — SQL** | Sql | `privatelink.documents.azure.com` | +| **Cosmos DB — MongoDB** | MongoDB | `privatelink.mongo.cosmos.azure.com` | +| **Cosmos DB — Cassandra** | Cassandra | `privatelink.cassandra.cosmos.azure.com` | +| **Cosmos DB — Gremlin** | Gremlin | `privatelink.gremlin.cosmos.azure.com` | +| **Cosmos DB — Table** | Table | `privatelink.table.cosmos.azure.com` | +| **Key Vault** | vault | `privatelink.vaultcore.azure.net` | +| **Key Vault — HSM** | managedhsm | `privatelink.managedhsm.azure.net` | +| **Azure Container Registry** | registry | `privatelink.azurecr.io` | +| **App Service / Functions** | sites | `privatelink.azurewebsites.net` | +| **Event Hubs** | namespace | `privatelink.servicebus.windows.net` | +| **Service Bus** | namespace | `privatelink.servicebus.windows.net` | +| **Azure Monitor** | azuremonitor | `privatelink.monitor.azure.com` | +| **Azure Cognitive Services** | account | `privatelink.cognitiveservices.azure.com` | +| **Azure OpenAI** | account | `privatelink.openai.azure.com` | +| **Azure Cache for Redis** | redisCache | `privatelink.redis.cache.windows.net` | +| **Azure Kubernetes Service** | management | `privatelink.{region}.azmk8s.io` | +| **Azure Data Factory** | dataFactory | `privatelink.datafactory.azure.net` | +| **Azure Synapse — SQL** | Sql | `privatelink.sql.azuresynapse.net` | +| **Azure Synapse — SqlOnDemand** | SqlOnDemand | `privatelink.sql.azuresynapse.net` | +| **Azure Synapse — Dev** | Dev | `privatelink.dev.azuresynapse.net` | +| **Azure Machine Learning** | amlworkspace | `privatelink.api.azureml.ms` | +| **Azure Batch** | batchAccount | `privatelink.{region}.batch.azure.com` | +| **Azure SignalR** | signalr | `privatelink.service.signalr.net` | +| **Azure IoT Hub** | iotHub | `privatelink.azure-devices.net` | +| **Azure Event Grid — Topic** | topic | `privatelink.eventgrid.azure.net` | +| **Azure Event Grid — Domain** | domain | `privatelink.eventgrid.azure.net` | + +> **Note:** Some services like AKS and Batch include the region in the zone name. Always verify the zone name in the Azure documentation for your specific service. + +## Custom DNS Server Configuration + +If your VNet uses a custom DNS server (not Azure-provided DNS), you must configure it to forward `privatelink.*` queries to Azure DNS: + +**Option A — Forward to Azure DNS directly (168.63.129.16):** + +On your DNS server, create conditional forwarders for each `privatelink.*` zone that point to `168.63.129.16`. This IP is the Azure platform DNS and is reachable from any Azure VM. + +**Option B — Forward to Azure DNS Private Resolver:** + +Deploy an Azure DNS Private Resolver in a VNet linked to your Private DNS zones. Forward queries from your custom DNS server to the resolver's inbound endpoint IP. + +```bash +# Create DNS Private Resolver +az dns-resolver create -g MyRG -n MyResolver \ + --id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/HubVNet + +# Create inbound endpoint (receives queries from your DNS server) +az dns-resolver inbound-endpoint create -g MyRG \ + --dns-resolver-name MyResolver -n InboundEndpoint \ + --ip-configurations '[{"private-ip-allocation-method":"Dynamic","id":"/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/HubVNet/subnets/InboundSubnet"}]' +``` + +## On-Premises DNS Forwarding Patterns + +For hybrid environments where on-premises clients must resolve private endpoint FQDNs to private IPs: + +### Pattern 1: Conditional Forwarding to Azure DNS Private Resolver + +``` +On-Prem Client → On-Prem DNS Server + → Conditional Forwarder (privatelink.blob.core.windows.net → Resolver inbound IP) + → Azure DNS Private Resolver → Private DNS Zone → 10.0.1.5 +``` + +This is the recommended pattern. The DNS Private Resolver is a managed service that requires no VM maintenance. Configure your on-premises DNS to forward all `privatelink.*` zones to the resolver's inbound endpoint IP. + +### Pattern 2: Conditional Forwarding to DNS Forwarder VMs + +``` +On-Prem Client → On-Prem DNS Server + → Conditional Forwarder (privatelink.blob.core.windows.net → Forwarder VM IP) + → DNS Forwarder VM (forwards to 168.63.129.16) → Private DNS Zone → 10.0.1.5 +``` + +This older pattern uses Windows DNS or BIND VMs in Azure that forward to 168.63.129.16. The Azure platform DNS (168.63.129.16) is only reachable from within Azure, so on-premises DNS cannot query it directly — hence the forwarder VM requirement. + +### On-Premises Conditional Forwarder Example (Windows DNS) + +Create conditional forwarders on your on-premises DNS for every `privatelink.*` zone you use: + +```powershell +# Point each privatelink zone to the Azure DNS Private Resolver inbound endpoint +Add-DnsServerConditionalForwarderZone -Name "privatelink.blob.core.windows.net" -MasterServers 10.0.0.4 +Add-DnsServerConditionalForwarderZone -Name "privatelink.database.windows.net" -MasterServers 10.0.0.4 +Add-DnsServerConditionalForwarderZone -Name "privatelink.vaultcore.azure.net" -MasterServers 10.0.0.4 +``` + +## Hub-Spoke DNS Architecture for Private Endpoints + +In a hub-spoke topology, centralize Private DNS zones in the hub: + +``` +Spoke VNet A (workload) ─── peered ──→ Hub VNet +Spoke VNet B (workload) ─── peered ──→ Hub VNet + │ + Private DNS Zones + (privatelink.*.*) + │ + Linked to Hub VNet + Linked to Spoke A VNet + Linked to Spoke B VNet +``` + +**Key design points:** + +1. Create all `privatelink.*` Private DNS zones in a central resource group (e.g., in the hub subscription). +2. Link each Private DNS zone to **every VNet** that needs to resolve private endpoint FQDNs — the hub and all spokes. +3. Private endpoints can live in spoke VNets. The DNS zone group on those endpoints creates A records in the centralized Private DNS zones. +4. If using custom DNS (e.g., Active Directory DNS in the hub), configure conditional forwarding from the custom DNS to Azure DNS (168.63.129.16) or to an Azure DNS Private Resolver. +5. Use Azure Policy to enforce that private endpoints always create DNS zone groups in the centralized zones. + +## Split-Horizon DNS Behavior + +When a private endpoint exists, Azure modifies public DNS to include a CNAME to `privatelink.*`: + +- **From inside a VNet with a linked Private DNS zone:** the query resolves to the private IP (10.x.x.x). +- **From the public internet (or a VNet without the linked zone):** the query falls through to public DNS and resolves to the public IP. + +This split-horizon behavior means: + +- The same FQDN (`mystorageaccount.blob.core.windows.net`) resolves differently depending on where the query originates. +- You don't need to change application connection strings. The FQDN stays the same — only DNS resolution changes. +- If you disable public access on the PaaS resource, external clients get a private IP resolution (if they use a linked zone) or a public IP that is blocked (if they don't have the zone). + +## Troubleshooting DNS for Private Endpoints + +### 1. Verify DNS Resolution with nslookup + +Run from a VM inside the VNet: + +```bash +nslookup mystorageaccount.blob.core.windows.net +``` + +**Expected output (working):** + +``` +Name: mystorageaccount.privatelink.blob.core.windows.net +Address: 10.0.1.5 +Aliases: mystorageaccount.blob.core.windows.net +``` + +**Broken output (resolves to public IP):** + +``` +Name: blob.bl4prdstr05a.store.core.windows.net +Address: 52.239.xxx.xxx +Aliases: mystorageaccount.blob.core.windows.net + mystorageaccount.privatelink.blob.core.windows.net +``` + +### 2. Common Issues and Fixes + +| Symptom | Cause | Fix | +|---------|-------|-----| +| Resolves to public IP from VNet | Private DNS zone not linked to VNet | Link the zone to the VNet | +| Resolves to public IP from VNet | No A record in Private DNS zone | Create a DNS zone group on the private endpoint | +| Resolves to public IP from VNet | VNet using custom DNS that doesn't forward to Azure | Configure conditional forwarding on custom DNS | +| On-prem resolves to public IP | No conditional forwarder for privatelink zone | Add conditional forwarder to DNS Private Resolver or forwarder VM | +| `NXDOMAIN` for privatelink FQDN | Private DNS zone doesn't exist | Create the Private DNS zone | +| Old cached public IP | DNS cache on client or intermediary DNS | Flush DNS cache: `ipconfig /flushdns` (Windows) or `sudo systemd-resolve --flush-caches` (Linux) | +| Works from one VNet but not another | DNS zone linked to only one VNet | Link the Private DNS zone to all VNets that need resolution | +| Private endpoint shows Approved but no connectivity | DNS resolves correctly but NSG blocks traffic | Check NSG rules on the private endpoint subnet (if NSG enforcement is enabled) | + +### 3. Verify the A Record Exists in the Private DNS Zone + +```bash +az network private-dns record-set a list \ + -g MyDnsRG \ + --zone-name privatelink.blob.core.windows.net \ + -o table +``` + +### 4. Verify the DNS Zone Is Linked to the VNet + +```bash +az network private-dns link vnet list \ + -g MyDnsRG \ + --zone-name privatelink.blob.core.windows.net \ + -o table +``` + +### 5. Verify DNS Zone Group Exists on the Private Endpoint + +```bash +az network private-endpoint dns-zone-group list \ + -g MyRG \ + --endpoint-name MyStoragePE \ + -o table +``` + +### 6. Test from Inside the VNet (Azure Bastion or SSH) + +Always test DNS resolution from a VM inside the VNet — not from Cloud Shell or your local machine, which are outside the VNet and won't see the Private DNS zone. + +```bash +# From a VM in the VNet: +dig mystorageaccount.blob.core.windows.net +curl -I https://mystorageaccount.blob.core.windows.net +``` + +If `dig` returns the private IP but `curl` fails with a connection timeout, the issue is likely NSG or routing — not DNS. diff --git a/plugin/skills/azure-private-link/references/private-link-service.md b/plugin/skills/azure-private-link/references/private-link-service.md new file mode 100644 index 000000000..a94f5e1a4 --- /dev/null +++ b/plugin/skills/azure-private-link/references/private-link-service.md @@ -0,0 +1,284 @@ +# Private Link Service Guide + +Azure Private Link Service allows you to expose your own application — running behind a Standard Load Balancer — as a private endpoint destination. Consumers in other VNets, subscriptions, or tenants can create private endpoints that connect to your service over the Microsoft backbone, without any public internet exposure. + +## Architecture Overview + +``` +Consumer Side Provider Side +───────────── ───────────── + +Consumer VNet Provider VNet +┌──────────────────┐ ┌──────────────────────────┐ +│ │ │ │ +│ Consumer App │ │ Private Link Service │ +│ │ │ │ │ │ +│ ▼ │ │ ▼ │ +│ Private │ Microsoft Backbone │ Standard Internal LB │ +│ Endpoint ──────┼─────────────────────────────┼──► │ │ +│ (10.1.0.5) │ │ ▼ │ +│ │ │ Backend VMs / VMSS │ +└──────────────────┘ │ (your application) │ + └──────────────────────────┘ +``` + +The consumer sees only a private IP address in their own VNet. They have no visibility into the provider's network topology, IP addresses, or infrastructure. The Private Link Service uses NAT to translate between the consumer's private endpoint IP and the provider's load balancer frontend. + +## Requirements + +1. **Standard Load Balancer** — Private Link Service only works with Standard SKU internal load balancers (not Basic, not public). +2. **Dedicated subnet** — The PLS requires a subnet in the provider VNet. This subnet can be shared with other PLS instances but should not host other resources. +3. **Disable network policies on the PLS subnet** — Network policies (NSGs, UDRs) must be disabled on the subnet used for PLS NAT IPs: + +```bash +az network vnet subnet update \ + -g ProviderRG \ + --vnet-name ProviderVNet \ + -n PLSSubnet \ + --disable-private-link-service-network-policies true +``` + +4. **At least one NAT IP configuration** — The PLS needs one or more NAT IPs from the PLS subnet for address translation. + +## NAT IP Configuration + +The Private Link Service uses NAT (Network Address Translation) to map consumer private endpoint IPs to the provider's load balancer frontend. NAT IPs are allocated from the PLS subnet. + +### Static vs Dynamic NAT IPs + +| Mode | Behavior | Use Case | +|------|----------|----------| +| **Dynamic** | Azure assigns an IP from the subnet | Default, simplest setup | +| **Static** | You specify the exact IP from the subnet | When you need predictable IPs for firewall rules or logging | + +### NAT IP Limits + +- **Maximum 8 NAT IPs** per Private Link Service +- Each NAT IP supports approximately 64,000 TCP connections (port space) +- For high-connection-count scenarios, add more NAT IPs (up to 8 × 64K = ~512K connections) + +## Creating a Private Link Service Step-by-Step + +### Step 1: Ensure You Have a Standard Internal Load Balancer + +```bash +# Create an internal Standard LB (if not already present) +az network lb create \ + -g ProviderRG \ + -n ProviderInternalLB \ + --sku Standard \ + --vnet-name ProviderVNet \ + --subnet BackendSubnet \ + --frontend-ip-name ProviderFrontend \ + --backend-pool-name ProviderBackendPool \ + --private-ip-address 10.0.1.10 +``` + +### Step 2: Disable Private Link Service Network Policies on the PLS Subnet + +```bash +az network vnet subnet update \ + -g ProviderRG \ + --vnet-name ProviderVNet \ + -n PLSSubnet \ + --disable-private-link-service-network-policies true +``` + +### Step 3: Create the Private Link Service + +```bash +az network private-link-service create \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --vnet-name ProviderVNet \ + --subnet PLSSubnet \ + --lb-name ProviderInternalLB \ + --lb-frontend-ip-configs ProviderFrontend \ + --private-ip-address 10.0.2.5 \ + --private-ip-address-version IPv4 \ + --private-ip-allocation-method Static +``` + +### Step 4: Note the PLS Alias for Consumer Use + +```bash +az network private-link-service show \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --query alias -o tsv +``` + +The alias looks like: `MyPrivateLinkService.{guid}.{region}.azure.privatelinkservice` + +Provide this alias to consumers. They use it to create private endpoints connecting to your service without needing your resource ID. + +### Step 5: Consumer Creates a Private Endpoint Using the Alias + +```bash +# Run by the consumer in their own subscription +az network private-endpoint create \ + -g ConsumerRG \ + -n ConsumerPE \ + --vnet-name ConsumerVNet \ + --subnet ConsumerSubnet \ + --private-connection-resource-id "" \ + --manual-request-connection-id MyPrivateLinkService.{guid}.{region}.azure.privatelinkservice \ + --connection-name ToProviderService \ + --request-message "Please approve our connection" +``` + +Alternatively, if the consumer has the full resource ID of the PLS (same-tenant or shared ID): + +```bash +az network private-endpoint create \ + -g ConsumerRG \ + -n ConsumerPE \ + --vnet-name ConsumerVNet \ + --subnet ConsumerSubnet \ + --private-connection-resource-id /subscriptions/{providerSub}/resourceGroups/ProviderRG/providers/Microsoft.Network/privateLinkServices/MyPrivateLinkService \ + --connection-name ToProviderService +``` + +## Visibility and Auto-Approval Configuration + +### Visibility + +Visibility controls which subscriptions can **discover** your Private Link Service. Options: + +| Setting | Behavior | +|---------|----------| +| No visibility list (default) | Any subscription with Azure RBAC on the PLS resource ID or alias can request a connection | +| Specific subscription IDs | Only listed subscriptions can see and connect to the PLS | +| `*` (all) | Any subscription can see the PLS (useful for marketplace offerings) | + +```bash +# Restrict visibility to specific subscriptions +az network private-link-service update \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --visibility subscription1-id subscription2-id +``` + +### Auto-Approval + +Auto-approval controls which subscriptions have their private endpoint connections **automatically approved** without manual intervention: + +```bash +# Auto-approve connections from specific subscriptions +az network private-link-service update \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --auto-approval subscription1-id subscription2-id +``` + +Connections from subscriptions not in the auto-approval list remain in **Pending** state until manually approved. + +## Proxy Protocol v2 for Source IP Preservation + +By default, the PLS performs NAT, which hides the consumer's original source IP. If your backend application needs the consumer's real IP (e.g., for logging or access control), enable Proxy Protocol v2: + +```bash +az network private-link-service update \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --enable-proxy-protocol true +``` + +When enabled, the PLS prepends a Proxy Protocol v2 header to every TCP connection. Your backend application must parse this header to extract: + +- **Source address** — the consumer's private endpoint IP (in the consumer's VNet) +- **Destination address** — the PLS NAT IP +- **Link ID** — unique identifier for the private endpoint connection + +> **Important:** Your application (or load balancer/reverse proxy behind the Standard LB) must support Proxy Protocol v2 parsing. If it doesn't, connections will appear malformed. Common applications with PP v2 support: HAProxy, NGINX (with `proxy_protocol`), and custom TCP servers. + +## Service Limits + +| Limit | Value | +|-------|-------| +| Private Link Services per subscription | 800 | +| NAT IP configurations per PLS | 8 | +| Private endpoint connections per PLS | 1,000 | +| Subscriptions in visibility list | 100 | +| Subscriptions in auto-approval list | 100 | +| TCP connections per NAT IP | ~64,000 | + +## Troubleshooting + +### Connection Stuck in Pending State + +**Cause:** The consumer's subscription is not in the auto-approval list, and the provider hasn't manually approved. + +**Fix:** The provider must approve the connection: + +```bash +# List pending connections +az network private-link-service show \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --query "privateEndpointConnections[?privateLinkServiceConnectionState.status=='Pending']" \ + -o table + +# Approve a specific connection +az network private-endpoint-connection approve \ + --resource-name MyPrivateLinkService \ + -g ProviderRG \ + --type Microsoft.Network/privateLinkServices \ + -n {connection-name} +``` + +### NAT Port Exhaustion + +**Symptoms:** New connections fail or time out; existing connections work fine. + +**Cause:** A single NAT IP has ~64,000 ports. If your service handles very high connection counts, you can exhaust the NAT port space. + +**Fix:** Add more NAT IPs (up to 8 per PLS): + +```bash +az network private-link-service update \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --ip-configs '[ + {"name":"natIp1","private-ip-address":"10.0.2.5","private-ip-allocation-method":"Static","subnet":{"id":"/subscriptions/{sub}/resourceGroups/ProviderRG/providers/Microsoft.Network/virtualNetworks/ProviderVNet/subnets/PLSSubnet"},"primary":true}, + {"name":"natIp2","private-ip-address":"10.0.2.6","private-ip-allocation-method":"Static","subnet":{"id":"/subscriptions/{sub}/resourceGroups/ProviderRG/providers/Microsoft.Network/virtualNetworks/ProviderVNet/subnets/PLSSubnet"},"primary":false} + ]' +``` + +### Consumer Cannot Resolve the Private Endpoint + +If the consumer creates a private endpoint to your PLS but can't connect: + +1. **DNS is not configured.** Unlike PaaS private endpoints, Private Link Service private endpoints do NOT have automatic public DNS CNAME insertion. The consumer must manually configure DNS (private DNS zone, hosts file, or custom DNS) to map a hostname to the private endpoint IP. + +2. **Connection is not approved.** Check the connection state: + +```bash +az network private-endpoint show \ + -g ConsumerRG \ + -n ConsumerPE \ + --query "manualPrivateLinkServiceConnections[0].privateLinkServiceConnectionState" \ + -o json +``` + +3. **Backend health probe failing.** The PLS forwards traffic to the Standard LB. If backend VMs are unhealthy, traffic is blackholed. Check LB health probes: + +```bash +az network lb show \ + -g ProviderRG \ + -n ProviderInternalLB \ + --query "loadBalancingRules[].backendAddressPool" -o json +``` + +### Consumer Gets Connection Reset + +**Cause:** Proxy Protocol v2 is enabled on the PLS but the backend application doesn't parse the PP v2 header. + +**Fix:** Either configure the backend to parse Proxy Protocol v2, or disable it on the PLS if source IP preservation is not required: + +```bash +az network private-link-service update \ + -g ProviderRG \ + -n MyPrivateLinkService \ + --enable-proxy-protocol false +``` diff --git a/plugin/skills/azure-route-server/SKILL.md b/plugin/skills/azure-route-server/SKILL.md new file mode 100644 index 000000000..d99f4e67f --- /dev/null +++ b/plugin/skills/azure-route-server/SKILL.md @@ -0,0 +1,107 @@ +--- +name: azure-route-server +description: "Deploy and manage Azure Route Server for dynamic BGP-based routing between network virtual appliances (NVAs) and Azure virtual networks. WHEN: route server, BGP peering, NVA routing, dynamic routing, branch-to-branch, RouteServerSubnet. DO NOT USE FOR: static route tables (use azure-virtual-network UDRs), VPN connectivity (use azure-vpn-gateway), DNS routing (use azure-traffic-manager), hub-spoke managed routing (use azure-virtual-wan)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Route Server Skill + +## When to Use This Skill + +- User wants to enable dynamic routing between NVAs and Azure VNets via BGP +- User needs route exchange between NVAs and Azure VPN/ExpressRoute gateways +- User asks about branch-to-branch transit through NVAs +- User wants to deploy a route server in a hub VNet +- User needs to troubleshoot BGP peering or route propagation issues +- User asks about RouteServerSubnet requirements + +## Rules + +1. Route Server requires a dedicated subnet named `RouteServerSubnet` with /27 or larger prefix. +2. Route Server uses ASN 65515 — NVA peers must use a different ASN (not 65515). +3. NVAs must peer with both Route Server instances (two IPs) for redundancy. +4. Enable branch-to-branch only when you need transit between VPN/ExpressRoute and NVAs. +5. Route Server supports up to 8 BGP peers. +6. Route Server does NOT forward data-plane traffic — it only exchanges routes. +7. Routes learned via Route Server are injected into the VNet's effective routes as "Virtual Network Gateway" type. +8. Route Server and VPN Gateway/ExpressRoute Gateway CAN coexist in the same VNet. +9. BGP peering is established over private IPs — NVA must be in the same VNet (or peered VNet). +10. Route Server supports multi-homed NVAs — it can learn the same prefix from multiple NVAs and program ECMP. + +## MCP Tools + +> Azure Route Server has limited MCP tool support. Use CLI commands for all operations. + +## CLI Fallback + +```bash +# Create RouteServerSubnet +az network vnet subnet create -g MyRG --vnet-name HubVNet -n RouteServerSubnet \ + --address-prefix 10.0.1.0/27 + +# Create public IP for Route Server +az network public-ip create -g MyRG -n RouteServerIP --sku Standard --allocation-method Static + +# Create Route Server +az network routeserver create -g MyRG -n MyRouteServer --hosted-subnet \ + /subscriptions/{sub}/resourceGroups/MyRG/providers/Microsoft.Network/virtualNetworks/HubVNet/subnets/RouteServerSubnet \ + --public-ip-address RouteServerIP + +# Add BGP peer (NVA) +az network routeserver peering create -g MyRG --routeserver MyRouteServer \ + -n NVAPeer1 --peer-asn 65001 --peer-ip 10.0.2.4 + +# List BGP peers +az network routeserver peering list -g MyRG --routeserver MyRouteServer -o table + +# Show learned routes from a peer +az network routeserver peering list-learned-routes -g MyRG --routeserver MyRouteServer -n NVAPeer1 + +# Show advertised routes to a peer +az network routeserver peering list-advertised-routes -g MyRG --routeserver MyRouteServer -n NVAPeer1 + +# Enable branch-to-branch transit +az network routeserver update -g MyRG -n MyRouteServer --allow-b2b-traffic true + +# Show Route Server details +az network routeserver show -g MyRG -n MyRouteServer +az network routeserver list -g MyRG -o table +``` + +## Key Concepts + +### Route Server Architecture + +| Component | Purpose | +|-----------|---------| +| Route Server | Managed BGP route reflector in your VNet | +| RouteServerSubnet | Dedicated /27+ subnet for Route Server instances | +| BGP Peer | NVA that exchanges routes via BGP (eBGP with ASN 65515) | +| Branch-to-branch | Enables transit between VPN/ER gateways and NVA peers | + +### Route Precedence (when Route Server is deployed) + +| Priority | Route Source | Notes | +|----------|-------------|-------| +| 1 (highest) | Longest prefix match | More specific route wins regardless of source | +| 2 | UDR (User-Defined Route) | Static UDR overrides BGP for same prefix | +| 3 | BGP routes | Routes learned from NVAs via Route Server | +| 4 | System routes | Azure default routes | + +### Limits + +| Resource | Limit | +|----------|-------| +| BGP peers per Route Server | 8 | +| Routes per BGP peer | 10,000 (Standard), 100 (Basic) | +| Route Servers per VNet | 1 | +| Route Servers per subscription | 1 (Basic), unlimited (Standard) | +| Supported NVA ASN range | 1-64495 (not 65515, not 65520 for ER) | + +## References + +- [BGP Peering Guide](references/bgp-peering.md) +- [NVA Integration Patterns](references/nva-integration.md) diff --git a/plugin/skills/azure-route-server/references/bgp-peering.md b/plugin/skills/azure-route-server/references/bgp-peering.md new file mode 100644 index 000000000..52b389bc7 --- /dev/null +++ b/plugin/skills/azure-route-server/references/bgp-peering.md @@ -0,0 +1,243 @@ +# BGP Peering with Azure Route Server + +## Overview + +Azure Route Server acts as a managed BGP route reflector deployed inside your virtual network. It establishes eBGP peering sessions with network virtual appliances (NVAs) and exchanges routes dynamically — eliminating the need for manual UDR maintenance whenever network topology changes. + +Route Server does not sit in the data path. It only participates in the control plane by learning routes from NVAs and programming them into the VNet's effective route table, and by advertising VNet address space (and optionally VPN/ExpressRoute routes) back to the NVAs. + +## ASN Requirements + +### Route Server ASN + +Route Server always uses **ASN 65515**. This is fixed and cannot be changed. All peering sessions between Route Server and NVAs are eBGP sessions because the NVA must use a different ASN. + +### NVA ASN Selection + +| ASN Range | Usage | +|-----------|-------| +| 1–64495 | Valid for NVA peering with Route Server | +| 64512–65514 | Private ASN range — recommended for NVAs | +| 65515 | **Reserved** — Route Server's own ASN; cannot be used by peers | +| 65520 | **Reserved** — used by ExpressRoute; cannot be used by peers | +| 65521–65534 | Reserved by Azure for internal use | +| 65535 | IANA reserved | + +**Recommendation:** Use private ASNs in the range **64512–65514** for your NVAs. This avoids conflicts with public ASNs and Azure-reserved ranges. + +If your NVA needs to peer with external BGP neighbors using a public ASN, that same public ASN can be used to peer with Route Server as long as it falls within 1–64495. + +## Peering Setup Walkthrough + +### Prerequisites + +1. A virtual network with a subnet named `RouteServerSubnet` (/27 or larger). +2. A deployed Route Server instance in that subnet. +3. An NVA deployed in the same VNet (or a VNet-peered VNet) with BGP capability. +4. The NVA's private IP address and chosen ASN. + +### Step 1: Retrieve Route Server Peering IPs + +After creating the Route Server, retrieve the two peering IP addresses: + +```bash +az network routeserver show -g MyRG -n MyRouteServer --query "virtualRouterIps" -o tsv +``` + +This returns two IPs, for example `10.0.1.4` and `10.0.1.5`. These are the Route Server instance IPs that the NVA must peer with. + +### Step 2: Register the NVA as a BGP Peer on Route Server + +```bash +az network routeserver peering create -g MyRG --routeserver MyRouteServer \ + -n NVAPeer1 --peer-asn 65001 --peer-ip 10.0.2.4 +``` + +### Step 3: Configure BGP on the NVA + +On the NVA, configure two eBGP neighbor sessions: + +- **Neighbor 1:** Route Server IP `10.0.1.4`, remote ASN `65515` +- **Neighbor 2:** Route Server IP `10.0.1.5`, remote ASN `65515` + +The NVA should advertise its desired prefixes to both neighbors and accept routes from both. + +Example (generic BGP configuration pseudocode): + +```text +router bgp 65001 + neighbor 10.0.1.4 remote-as 65515 + neighbor 10.0.1.5 remote-as 65515 + network 172.16.0.0/16 +``` + +### Step 4: Verify Peering + +```bash +# Check peering status +az network routeserver peering list -g MyRG --routeserver MyRouteServer -o table + +# Check learned routes +az network routeserver peering list-learned-routes -g MyRG --routeserver MyRouteServer -n NVAPeer1 + +# Check advertised routes +az network routeserver peering list-advertised-routes -g MyRG --routeserver MyRouteServer -n NVAPeer1 +``` + +## Why You Must Peer with Both Route Server Instances + +Route Server is deployed as a pair of instances for high availability. Each instance has its own private IP in the RouteServerSubnet. If your NVA only peers with one instance: + +- **Failover gap:** If that instance undergoes maintenance or fails, all learned routes are withdrawn and traffic blackholes until the instance recovers. +- **Incomplete route view:** Each instance independently manages its BGP sessions. Peering with only one means the other instance has no routes from your NVA. + +**Always configure the NVA to peer with both IPs.** Both sessions should be active simultaneously — this is not an active/standby configuration. + +## Route Propagation Behavior + +When an NVA advertises a prefix (e.g., `172.16.0.0/16`) to Route Server: + +1. Route Server receives the BGP UPDATE from the NVA. +2. Route Server programs the route into the VNet's effective route table. +3. The route appears on all NICs in the VNet with source **"Virtual Network Gateway"** and next hop set to the NVA's private IP. +4. VMs in the VNet now send traffic destined for `172.16.0.0/16` to the NVA. + +In the reverse direction, Route Server advertises the VNet's address space and (if branch-to-branch is enabled) any VPN/ExpressRoute routes to the NVA. + +### What Route Server Advertises to NVAs + +| Condition | Routes Advertised | +|-----------|-------------------| +| Default | VNet address prefixes, peered VNet prefixes | +| Branch-to-branch enabled | Above + VPN Gateway learned routes + ExpressRoute learned routes | + +## Route Exchange with VPN and ExpressRoute Gateways + +When Route Server coexists with a VPN Gateway or ExpressRoute Gateway in the same VNet: + +- **Without branch-to-branch:** Gateways and Route Server operate independently. NVAs do not learn on-premises routes from the gateways. +- **With branch-to-branch enabled:** Route Server acts as a route reflector between the gateways and the NVAs. On-premises routes from VPN/ExpressRoute are advertised to NVA peers, and NVA routes are advertised to the gateways. + +This enables powerful transit scenarios — for example, traffic from an on-premises branch can reach an SD-WAN overlay network through an NVA in the hub VNet. + +```text +On-premises ←→ VPN/ER Gateway ←→ Route Server ←→ NVA ←→ SD-WAN branches +``` + +**Important:** Branch-to-branch causes all NVA routes to be advertised to on-premises via the gateway. Ensure your NVA only advertises prefixes you intend to be reachable from on-premises. + +## BGP Communities + +Route Server supports standard BGP communities on learned routes. NVAs can tag routes with communities, and Route Server will preserve them when programming routes. However, Route Server itself does not perform community-based filtering — it accepts all valid routes from configured peers. + +When branch-to-branch is enabled, communities from NVA routes are carried through to VPN/ExpressRoute advertisements and vice versa. This allows on-premises routers to make policy decisions based on communities set by the NVA. + +## Route Filtering + +### What Route Server Does + +- Accepts all valid BGP routes from configured peers (up to the per-peer route limit). +- Programs all learned routes into VNet effective routes. +- Advertises VNet and (optionally) gateway routes to NVA peers. + +### What Route Server Does NOT Do + +- Route Server does **not** support route maps or prefix lists. +- Route Server does **not** filter or modify routes in transit. +- Route Server does **not** perform route summarization. +- Route Server does **not** support conditional advertisement. + +All filtering must be performed on the NVA side using the NVA's own BGP policy capabilities. + +## Branch-to-Branch Transit + +### Enabling + +```bash +az network routeserver update -g MyRG -n MyRouteServer --allow-b2b-traffic true +``` + +### Use Cases + +- **SD-WAN integration:** On-premises branches reach SD-WAN branches through an NVA. +- **VPN-to-ExpressRoute transit:** Traffic between VPN-connected sites and ExpressRoute-connected sites transits through an NVA for inspection. +- **Multi-site NVA routing:** NVA aggregates routes from multiple remote sites and advertises them to Azure and on-premises. + +### Implications + +- Increases route table size on gateways (NVA routes are now advertised to on-premises). +- On-premises routers see NVA-advertised prefixes with ASN path: `65515 `. +- Traffic between on-premises and NVA destinations transits the NVA in the data plane — ensure NVA has sufficient throughput. +- If NVA advertises a default route (`0.0.0.0/0`), it will be propagated to on-premises — this can inadvertently redirect all on-premises internet traffic. + +## Multi-Homing and ECMP + +Route Server supports Equal-Cost Multi-Path (ECMP) routing. When two or more NVAs advertise the **same prefix** with the same AS path length: + +1. Route Server learns the route from each NVA. +2. Route Server programs **multiple next hops** for that prefix in the VNet effective routes. +3. Azure fabric distributes traffic across the NVAs using 5-tuple hash-based load balancing. + +This provides both redundancy and increased throughput without requiring a load balancer in front of the NVAs. + +**Example:** Two NVAs (10.0.2.4 and 10.0.2.5) both advertise `172.16.0.0/16` with ASN 65001. VMs in the VNet see two effective routes for `172.16.0.0/16`, each pointing to a different NVA. Traffic is distributed across both. + +### ECMP Requirements + +- Both NVAs must advertise the same prefix with the same AS path length. +- Both NVAs must be peered with Route Server. +- Maximum 8 ECMP paths (limited by the 8-peer maximum). + +## BGP Timers + +Route Server uses the following BGP timer defaults: + +| Timer | Value | +|-------|-------| +| Keepalive interval | 60 seconds | +| Hold time | 180 seconds | + +These timers are **not configurable** on Route Server. If the NVA does not send a keepalive within the hold time (180 seconds), Route Server declares the peer down and withdraws all routes learned from that peer. + +**Convergence note:** When an NVA fails, it can take up to 180 seconds for routes to be withdrawn. For faster failover, configure BFD (Bidirectional Forwarding Detection) on the NVA if supported, though note that Route Server itself does not support BFD — the NVA-side BFD can detect data-plane failures and withdraw BGP routes proactively. + +## Troubleshooting + +### Peering Not Establishing + +| Symptom | Possible Cause | Resolution | +|---------|---------------|------------| +| Peering stuck in "Connecting" | NVA not configured to peer with Route Server IPs | Configure NVA BGP neighbors for both Route Server IPs | +| Peering stuck in "Connecting" | NVA using ASN 65515 | Change NVA ASN — 65515 is reserved for Route Server | +| Peering stuck in "Connecting" | NSG blocking TCP port 179 | Ensure no NSG on RouteServerSubnet or NVA subnet blocks BGP (TCP 179) | +| Peering stuck in "Connecting" | NVA in a different VNet without peering | Enable VNet peering and ensure "Allow forwarded traffic" is set | +| Peering flapping | NVA overloaded, dropping keepalives | Investigate NVA CPU/memory; increase NVA capacity | + +### Routes Not Propagated + +| Symptom | Possible Cause | Resolution | +|---------|---------------|------------| +| NVA routes not in effective routes | NVA not advertising prefixes | Check NVA BGP config — ensure `network` statements or redistribution is configured | +| VNet routes not reaching NVA | Route Server not advertising VNet prefixes | Verify peering is established; check `list-advertised-routes` | +| On-premises routes not reaching NVA | Branch-to-branch not enabled | Enable with `--allow-b2b-traffic true` | +| Routes appearing then disappearing | NVA withdrawing routes | Check NVA logs for BGP withdrawal messages | +| Route limit exceeded | NVA advertising >10,000 routes | Reduce advertised prefixes or summarize on the NVA side | + +### Asymmetric Routing + +Asymmetric routing occurs when forward and return traffic take different paths. Common scenarios: + +- **NVA advertises a more specific prefix than expected:** Return traffic bypasses the intended path. Ensure NVA prefix advertisement matches the intended routing design. +- **UDR and Route Server conflict:** A UDR points traffic to NVA-A, but Route Server learns a route pointing to NVA-B. UDR wins for the same prefix — remove conflicting UDRs or align them. +- **Single-NVA peering:** Only one NVA is peered with Route Server. Traffic may arrive at the NVA via Route Server routes but return via a different path. Peer both NVAs. + +### Incorrect ASN Configuration + +If the NVA is configured with the wrong remote ASN (not 65515) for Route Server, the BGP OPEN message will be rejected. Verify: + +```bash +# On Route Server side — check peering configuration +az network routeserver peering list -g MyRG --routeserver MyRouteServer -o table + +# On NVA side — ensure remote-as is 65515 for both Route Server IPs +``` diff --git a/plugin/skills/azure-route-server/references/nva-integration.md b/plugin/skills/azure-route-server/references/nva-integration.md new file mode 100644 index 000000000..70cb32de3 --- /dev/null +++ b/plugin/skills/azure-route-server/references/nva-integration.md @@ -0,0 +1,295 @@ +# NVA Integration Patterns with Azure Route Server + +## Overview + +Azure Route Server enables dynamic routing between network virtual appliances (NVAs) and Azure virtual networks. Instead of maintaining static UDRs that must be updated whenever topology changes, Route Server allows NVAs to advertise and withdraw routes via BGP in real time. This document covers common NVA deployment patterns, configuration requirements, and troubleshooting guidance. + +## Common NVA Deployment Patterns + +### Single NVA Hub Pattern + +The simplest pattern: one NVA in a hub VNet peers with Route Server and advertises routes for remote networks (on-premises, other clouds, SD-WAN branches). + +```text +Spoke VNets ←(peering)→ Hub VNet + ├── Route Server (learns routes from NVA) + ├── NVA (BGP peer, next hop for remote prefixes) + └── (optional) VPN/ER Gateway +``` + +**How it works:** + +1. NVA establishes BGP peering with Route Server (both instances). +2. NVA advertises remote prefixes (e.g., `10.100.0.0/16` for on-premises). +3. Route Server programs these routes into the hub VNet effective route table. +4. Spoke VNets with "Use Remote Gateway" enabled on peering inherit these routes. +5. VMs in hub and spoke VNets send traffic for `10.100.0.0/16` to the NVA's private IP. + +**Limitation:** Single point of failure. If the NVA goes down, all dynamically learned routes are withdrawn after the BGP hold timer expires (up to 180 seconds). + +### Active-Active NVA Pair with ECMP + +Two identical NVAs peer with Route Server and advertise the same prefixes. Route Server programs ECMP routes with both NVAs as next hops. + +```text +Spoke VNets ←(peering)→ Hub VNet + ├── Route Server + ├── NVA-1 (10.0.2.4, ASN 65001) ──→ advertises 10.100.0.0/16 + └── NVA-2 (10.0.2.5, ASN 65001) ──→ advertises 10.100.0.0/16 +``` + +**Traffic distribution:** Azure fabric uses 5-tuple hashing (source IP, destination IP, source port, destination port, protocol) to distribute flows across both NVAs. Individual flows stick to one NVA; aggregate traffic is balanced. + +**Failover:** When one NVA fails, Route Server withdraws its routes after the hold timer (180s). All traffic shifts to the surviving NVA. Recovery is automatic when the failed NVA re-establishes peering. + +**Configuration notes:** + +- Both NVAs can use the same ASN (e.g., 65001). +- Both must advertise identical prefixes with the same AS path length for ECMP. +- Both must peer with both Route Server instances (4 total BGP sessions). + +### Multi-NVA for Different Route Domains + +Different NVAs handle different route domains — for example, one NVA for on-premises connectivity and another for internet/security inspection. + +```text +Hub VNet + ├── Route Server + ├── NVA-Firewall (ASN 65001) → advertises 0.0.0.0/0 (internet via firewall) + └── NVA-SDWAN (ASN 65002) → advertises 10.100.0.0/16 (SD-WAN branches) +``` + +Each NVA advertises only its relevant prefixes. Route Server programs all routes — VMs route internet traffic to the firewall NVA and SD-WAN traffic to the SD-WAN NVA based on longest prefix match. + +**Key consideration:** If both NVAs advertise overlapping prefixes, longest prefix match determines the winner. If prefixes are identical and AS path lengths differ, the shorter AS path wins. If everything is equal, both become ECMP next hops — which may not be desired if the NVAs serve different functions. + +## Route Server + SD-WAN NVA Pattern + +SD-WAN appliances (Cisco Viptela, VMware SD-WAN, Versa, Silver Peak) commonly integrate with Route Server to: + +1. Advertise SD-WAN branch prefixes into the Azure VNet. +2. Learn Azure VNet and on-premises (VPN/ExpressRoute) prefixes to advertise to SD-WAN branches. + +**Architecture:** + +```text +SD-WAN branches ←(overlay)→ SD-WAN NVA in Hub VNet ←(BGP)→ Route Server + ↓ + VNet effective routes + (spoke VMs reach branches) +``` + +**With branch-to-branch enabled:** + +```text +On-premises ←(VPN/ER)→ Gateway ←(Route Server)→ SD-WAN NVA ←(overlay)→ SD-WAN branches +``` + +On-premises sites learn SD-WAN branch routes via the VPN/ExpressRoute gateway, and SD-WAN branches learn on-premises routes via the NVA. All transit traffic flows through the SD-WAN NVA. + +## Route Server + Firewall NVA Pattern + +Third-party firewall NVAs from Azure Marketplace (Palo Alto VM-Series, Fortinet FortiGate, Check Point CloudGuard, Cisco FTDv) can peer with Route Server to inject themselves as the next hop for inspected traffic. + +**Common approach:** + +1. Firewall NVA advertises a default route (`0.0.0.0/0`) via BGP to Route Server. +2. Route Server programs the default route into VNet effective routes. +3. All internet-bound traffic from VMs flows through the firewall NVA. +4. Firewall NVA also advertises specific prefixes for east-west inspection between spokes. + +**Warning:** Advertising `0.0.0.0/0` from the NVA overrides the Azure default internet route for all VMs in the VNet and peered spoke VNets. This is powerful but affects all subnets — including those that may need direct internet access (e.g., Azure Bastion, Application Gateway). Use UDRs on specific subnets to override the NVA default route where needed. + +**Example — Palo Alto integration:** + +```bash +# 1. Deploy Palo Alto NVA from Marketplace (portal or Terraform) +# 2. Configure BGP on Palo Alto (ASN 65001, neighbors = Route Server IPs) +# 3. Register peer on Route Server +az network routeserver peering create -g MyRG --routeserver MyRouteServer \ + -n PaloAltoPeer --peer-asn 65001 --peer-ip 10.0.3.4 + +# 4. Verify +az network routeserver peering list-learned-routes -g MyRG \ + --routeserver MyRouteServer -n PaloAltoPeer +``` + +## Route Exchange Flow + +Understanding the full route exchange cycle is critical for debugging: + +```text +Step 1: NVA advertises prefix (e.g., 172.16.0.0/16) to Route Server via BGP +Step 2: Route Server programs 172.16.0.0/16 into VNet effective routes + → next hop = NVA private IP (e.g., 10.0.2.4) + → source = "Virtual Network Gateway" +Step 3: VM in VNet sends packet to 172.16.1.10 + → Azure fabric routes packet to 10.0.2.4 (the NVA) +Step 4: NVA receives the packet on its NIC and forwards it to the actual destination + → IP forwarding MUST be enabled on the NVA NIC +Step 5: Return traffic follows reverse path (or symmetric if correctly configured) +``` + +**Key insight:** Route Server only handles the control plane (step 1–2). The NVA handles the data plane (step 4). Azure fabric handles packet delivery between VMs and NVA (step 3). + +## NVA Requirements + +### BGP Support + +The NVA must support BGP (eBGP specifically) and be capable of: + +- Establishing eBGP sessions with ASN 65515 (Route Server's ASN). +- Advertising prefixes it wants to inject into the VNet. +- Accepting routes from Route Server (VNet and optionally gateway prefixes). + +### Network Connectivity + +| Requirement | Detail | +|-------------|--------| +| Location | NVA must be in the same VNet as Route Server, or in a VNet peered to it | +| IP addressing | NVA BGP peering uses the NVA's private IP (not public IP) | +| Peering peers | NVA must peer with both Route Server instance IPs | +| Port access | TCP 179 (BGP) must be allowed between NVA and RouteServerSubnet | + +### IP Forwarding — Critical Requirement + +**IP forwarding MUST be enabled on the NVA's NIC** for transit traffic to work. Without it, Azure fabric drops packets not destined for the NVA's own IP. + +```bash +# Enable IP forwarding on NVA NIC +az network nic update -g MyRG -n NVA-NIC --ip-forwarding true +``` + +This is the single most common cause of "routes are programmed but traffic doesn't flow through the NVA." + +## UDR Interaction with Route Server + +### When UDRs Are Still Needed + +Even with Route Server, UDRs are necessary in these scenarios: + +| Scenario | Why UDR Is Needed | +|----------|-------------------| +| Force traffic from specific subnets to NVA | Route Server programs routes VNet-wide; UDRs provide per-subnet control | +| Override an NVA-advertised default route on certain subnets | Azure Bastion, Application Gateway, and other PaaS services may break with NVA as default gateway | +| Ensure spoke-to-spoke traffic through NVA | Spoke-to-spoke traffic via VNet peering may bypass NVA; UDRs on spoke subnets ensure it transits the NVA | +| Static fallback route | If BGP peering fails, a UDR ensures critical traffic still has a path | + +### Route Precedence + +When both UDRs and Route Server routes exist for the same prefix: + +1. **UDR wins** for an exact prefix match. +2. **Longest prefix match** applies across all route sources — a more specific BGP route beats a less specific UDR. +3. If no UDR or BGP route matches, Azure system routes apply. + +### Comparing Route Server with UDRs + +| Dimension | Route Server (BGP) | UDRs | +|-----------|-------------------|------| +| Route updates | Automatic via BGP | Manual (or scripted/automated) | +| Failover | Automatic on BGP peer down | Requires manual or scripted switchover | +| Scope | VNet-wide | Per-subnet | +| Prefix limit | 10,000 per peer (Standard) | 400 per route table | +| NVA requirement | Must support BGP | No BGP needed | +| Complexity | Higher initial setup | Simple but harder to maintain at scale | + +## Hub-Spoke Architecture with Route Server + +In a hub-spoke topology: + +1. Route Server is deployed in the **hub VNet**. +2. NVAs are deployed in the **hub VNet** (same VNet as Route Server). +3. Spoke VNets peer with the hub using **"Use Remote Gateways"** (on spoke side) and **"Allow Gateway Transit"** (on hub side). +4. Routes learned by Route Server propagate to spoke VNets via the peering gateway transit setting. + +```text +Spoke-1 (10.1.0.0/16) ──┐ +Spoke-2 (10.2.0.0/16) ──┤── peering ──→ Hub VNet (10.0.0.0/16) +Spoke-3 (10.3.0.0/16) ──┘ ├── Route Server + ├── NVA (advertises 172.16.0.0/12) + └── VPN Gateway (optional) +``` + +VMs in all spokes see `172.16.0.0/12 → next hop 10.0.2.4 (NVA)` in their effective routes. + +**Important:** For spoke-to-spoke traffic to transit through the NVA, the NVA must advertise the spoke VNet prefixes back (or a summary that covers them). Alternatively, use UDRs on spoke subnets to force spoke-to-spoke traffic through the NVA. + +## Failover Behavior + +### What Happens When an NVA Goes Down + +1. The NVA stops sending BGP keepalives to Route Server. +2. Route Server waits for the hold timer to expire (**180 seconds**). +3. Route Server declares the peer down and withdraws all routes learned from that NVA. +4. VNet effective routes are updated — traffic shifts to any remaining ECMP next hops. +5. If no other NVA advertises the same prefix, the route is removed entirely and traffic falls back to the next matching route (UDR or system route). + +### Reducing Failover Time + +- **Deploy active-active NVAs with ECMP.** When one NVA fails, the other immediately handles all traffic — no waiting for hold timer on the surviving NVA. +- **NVA-side health monitoring:** Configure the NVA to proactively withdraw BGP routes if it detects data-plane failure (e.g., tunnel down, upstream unreachable). This triggers immediate Route Server convergence instead of waiting 180 seconds. +- **BFD on NVA side:** Some NVAs support BFD for fast failure detection. While Route Server does not support BFD, the NVA can use BFD with other peers and withdraw BGP routes to Route Server when BFD detects a failure. + +## Troubleshooting + +### Traffic Not Forwarding Through NVA + +| Check | Command / Action | +|-------|-----------------| +| IP forwarding enabled on NVA NIC? | `az network nic show -g MyRG -n NVA-NIC --query "enableIPForwarding"` | +| NVA receiving traffic? | Packet capture on NVA NIC | +| Effective routes show NVA as next hop? | `az network nic show-effective-route-table -g MyRG -n VM-NIC -o table` | +| NSG blocking traffic to/from NVA? | Check NSG rules on NVA subnet and VM subnet | +| NVA internal routing correct? | Verify NVA forwards packets out the correct interface | + +### NVA Not Receiving Traffic Despite Routes Being Programmed + +This almost always means **IP forwarding is not enabled** on the NVA NIC. Azure drops packets at the fabric level when a NIC receives traffic not addressed to its own IP and IP forwarding is disabled. + +```bash +# Fix +az network nic update -g MyRG -n NVA-NIC --ip-forwarding true +``` + +Also verify that the NVA's OS-level IP forwarding is enabled (e.g., `net.ipv4.ip_forward = 1` on Linux). + +### Route Conflicts + +When unexpected routing occurs: + +1. **Check effective routes** on the affected VM's NIC: + + ```bash + az network nic show-effective-route-table -g MyRG -n VM-NIC -o table + ``` + +2. **Identify conflicting sources.** Look for overlapping prefixes from different sources (UDR, BGP, system). + +3. **Apply precedence rules:** UDR beats BGP for the same prefix. Longest prefix match wins across sources. + +4. **Check Route Server learned routes** to understand what the NVA is advertising: + + ```bash + az network routeserver peering list-learned-routes -g MyRG \ + --routeserver MyRouteServer -n NVAPeer1 + ``` + +5. **Check Route Server advertised routes** to understand what the NVA is receiving: + + ```bash + az network routeserver peering list-advertised-routes -g MyRG \ + --routeserver MyRouteServer -n NVAPeer1 + ``` + +### Common Mistakes + +| Mistake | Impact | Fix | +|---------|--------|-----| +| IP forwarding not enabled on NVA NIC | Traffic dropped by Azure fabric | Enable IP forwarding on NVA NIC | +| NVA only peers with one Route Server IP | Partial route coverage, failover gap | Peer with both Route Server instance IPs | +| NVA advertises 0.0.0.0/0 without subnet-level UDR overrides | Breaks Azure Bastion, App Gateway, other PaaS | Add UDR with Internet next-hop on affected subnets | +| Spoke peering missing "Use Remote Gateways" | Spoke VMs don't learn Route Server routes | Enable gateway transit on both sides of peering | +| NVA using ASN 65515 | BGP peering never establishes | Change NVA ASN to a different value (64512–65514 recommended) | +| NSG on RouteServerSubnet blocking TCP 179 | BGP peering cannot establish | Allow TCP 179 inbound/outbound on RouteServerSubnet NSG | +| NVA OS-level forwarding disabled | Packets arrive at NVA but are dropped by OS | Enable `ip_forward` (Linux) or routing role (Windows) | diff --git a/plugin/skills/azure-traffic-manager/SKILL.md b/plugin/skills/azure-traffic-manager/SKILL.md new file mode 100644 index 000000000..366728469 --- /dev/null +++ b/plugin/skills/azure-traffic-manager/SKILL.md @@ -0,0 +1,132 @@ +--- +name: azure-traffic-manager +description: "Configure Azure Traffic Manager for DNS-based global traffic routing with priority, weighted, performance, geographic, multivalue, and subnet routing methods. WHEN: traffic manager, DNS load balancing, geographic routing, priority routing, weighted routing, performance routing, failover, global DNS. DO NOT USE FOR: HTTP/HTTPS load balancing (use azure-front-door or azure-application-gateway), private link connectivity (use azure-private-link), network-level load balancing (use azure-load-balancer)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Traffic Manager Skill + +## When to Use This Skill + +- User needs DNS-based global traffic distribution across Azure regions or external endpoints +- User wants active-passive failover with automatic health checking +- User asks about geographic routing to direct users to region-specific endpoints +- User needs weighted distribution of traffic across multiple endpoints +- User wants performance-based routing to send users to the lowest-latency endpoint +- User needs to configure health probes for endpoint monitoring +- User asks about nested Traffic Manager profiles for complex routing +- User wants to understand the difference between Traffic Manager and other load balancers + +## Rules + +1. Traffic Manager is DNS-based — it returns a DNS name/IP; it does NOT proxy traffic. +2. DNS TTL affects failover time — lower TTL (30s) = faster failover, more DNS queries; default is 60s. +3. Health probes are critical — an unhealthy endpoint is removed from DNS responses within ~30-60 seconds. +4. Geographic routing assigns regions to endpoints — every query MUST map to an endpoint or it gets no answer. +5. Priority routing: lower priority value = higher precedence (1 is first choice). +6. Nested profiles allow combining routing methods (e.g., performance at outer, weighted at inner level). +7. Traffic Manager works with any internet-facing endpoint — Azure, on-premises, other clouds. +8. External endpoints require health probe accessibility from the internet. +9. For HTTP/HTTPS applications, consider Azure Front Door instead — it provides TLS termination and caching. +10. Traffic Manager is NOT a proxy or gateway — source IP seen by the backend is the client's IP. + +## MCP Tools + +| Tool | Command | Purpose | +|------|---------|---------| +| `azure__trafficmanager` | `profile_list` | List all Traffic Manager profiles in a subscription | +| `azure__trafficmanager` | `profile_get` | Get details of a Traffic Manager profile including endpoints and settings | + +## CLI Fallback + +```bash +# Create Traffic Manager profile (priority routing) +az network traffic-manager profile create -g MyRG -n MyTMProfile \ + --routing-method Priority --unique-dns-name myapp-tm \ + --ttl 60 --protocol HTTPS --port 443 --path /health + +# Create Traffic Manager profile (weighted routing) +az network traffic-manager profile create -g MyRG -n MyWeightedTM \ + --routing-method Weighted --unique-dns-name myapp-weighted \ + --ttl 30 --protocol HTTPS --port 443 --path /health + +# Add Azure endpoint +az network traffic-manager endpoint create -g MyRG --profile-name MyTMProfile \ + -n EastUSEndpoint --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-eastus \ + --priority 1 --endpoint-status Enabled + +# Add external endpoint +az network traffic-manager endpoint create -g MyRG --profile-name MyTMProfile \ + -n OnPremEndpoint --type externalEndpoints \ + --target onprem.contoso.com --priority 2 --endpoint-status Enabled + +# Add nested profile endpoint +az network traffic-manager endpoint create -g MyRG --profile-name MyTMProfile \ + -n NestedEndpoint --type nestedEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/trafficManagerProfiles/InnerProfile \ + --priority 3 --min-child-endpoints 1 + +# Add geographic endpoint +az network traffic-manager endpoint create -g MyRG --profile-name MyGeoTM \ + -n EuropeEndpoint --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-europe \ + --geo-mapping "GEO-EU" + +# Show profile details +az network traffic-manager profile show -g MyRG -n MyTMProfile +az network traffic-manager profile list -g MyRG -o table + +# Show endpoint details +az network traffic-manager endpoint show -g MyRG --profile-name MyTMProfile -n EastUSEndpoint --type azureEndpoints + +# Update health probe settings +az network traffic-manager profile update -g MyRG -n MyTMProfile \ + --protocol HTTPS --port 443 --path /health \ + --interval 30 --timeout 10 --tolerated-failures 3 + +# Disable/enable an endpoint +az network traffic-manager endpoint update -g MyRG --profile-name MyTMProfile \ + -n EastUSEndpoint --type azureEndpoints --endpoint-status Disabled +``` + +## Key Concepts + +### Routing Methods + +| Method | Use When | How It Works | +|--------|----------|-------------| +| Priority | Active-passive failover | Returns highest priority (lowest value) healthy endpoint | +| Weighted | A/B testing, gradual migration | Distributes traffic by weight ratio | +| Performance | Lowest latency to user | Uses Azure latency table to find closest endpoint | +| Geographic | Data residency, regional compliance | Maps geographic regions to specific endpoints | +| MultiValue | Multiple healthy IPs needed | Returns all healthy endpoints (up to MaxReturn) | +| Subnet | Client IP-based routing | Maps client subnet ranges to specific endpoints | + +### Health Probe Settings + +| Setting | Default | Range | Notes | +|---------|---------|-------|-------| +| Protocol | HTTP | HTTP, HTTPS, TCP | HTTPS recommended for web apps | +| Port | 80 | 1-65535 | Must match application port | +| Path | / | Any valid path | Should return 200 when healthy | +| Interval | 30s | 10-300s | Time between probes | +| Timeout | 10s | 5-10s | Time to wait for response | +| Tolerated failures | 3 | 0-9 | Failures before marking unhealthy | + +### Failover Timing Calculation + +``` +Failover time ≈ (Probe interval × Tolerated failures) + Probe interval + DNS TTL +Example: (30s × 3) + 30s + 60s = 180 seconds (3 minutes) +Fast config: (10s × 1) + 10s + 30s = 50 seconds +``` + +## References + +- [Routing Methods Guide](references/routing-methods.md) +- [Endpoint Types](references/endpoint-types.md) +- [Health Checks Configuration](references/health-checks.md) diff --git a/plugin/skills/azure-traffic-manager/references/endpoint-types.md b/plugin/skills/azure-traffic-manager/references/endpoint-types.md new file mode 100644 index 000000000..8a00f7c3c --- /dev/null +++ b/plugin/skills/azure-traffic-manager/references/endpoint-types.md @@ -0,0 +1,262 @@ +# Traffic Manager Endpoint Types + +Traffic Manager supports three endpoint types: Azure endpoints, external endpoints, and nested endpoints. Each type connects Traffic Manager to a different kind of backend resource. + +--- + +## Azure Endpoints + +Azure endpoints connect Traffic Manager to resources hosted within Azure. Traffic Manager resolves the endpoint directly using the Azure resource's metadata. + +### Supported Resource Types + +| Resource Type | Provider Path | Notes | +|---------------|--------------|-------| +| App Service (Web Apps) | `Microsoft.Web/sites` | Most common; must be Standard tier or higher | +| Cloud Service | `Microsoft.ClassicCompute/domainNames` | Classic deployment model | +| Public IP Address | `Microsoft.Network/publicIPAddresses` | Must be static; typically attached to a VM or load balancer | +| App Service Slot | `Microsoft.Web/sites/slots` | Route to a specific deployment slot | + +### Finding the Target Resource ID + +The `--target-resource-id` is the full Azure Resource Manager path to the resource: + +```bash +# Find App Service resource ID +az webapp show -g MyRG -n myapp-eastus --query id -o tsv +# Output: /subscriptions/aaaa-bbbb/resourceGroups/MyRG/providers/Microsoft.Web/sites/myapp-eastus + +# Find Public IP resource ID +az network public-ip show -g MyRG -n myapp-pip --query id -o tsv + +# Use directly in endpoint creation +az network traffic-manager endpoint create -g MyRG --profile-name MyTMProfile \ + -n EastUSEndpoint --type azureEndpoints \ + --target-resource-id $(az webapp show -g MyRG -n myapp-eastus --query id -o tsv) \ + --priority 1 +``` + +### Cross-Subscription Endpoints + +Azure endpoints can reference resources in a different subscription than the Traffic Manager profile. The user configuring the endpoint needs read access to the target resource. + +```bash +# Reference an App Service in a different subscription +az network traffic-manager endpoint create -g MyRG --profile-name MyTMProfile \ + -n CrossSubEndpoint --type azureEndpoints \ + --target-resource-id /subscriptions/OTHER-SUB-ID/resourceGroups/OtherRG/providers/Microsoft.Web/sites/other-app \ + --priority 2 +``` + +### Automatic Region Detection + +For Azure endpoints, Traffic Manager automatically determines the endpoint's Azure region from the resource metadata. This is used by Performance routing to make latency-based decisions. You do NOT need to set `--endpoint-location` for Azure endpoints. + +### App Service Requirements + +- App Service must be on a Standard, Premium, or Isolated tier (Free and Basic tiers do not support Traffic Manager integration). +- Each App Service must have a unique DNS name within `.azurewebsites.net`. +- When using multiple App Services across regions, each must be a separate App Service plan in its target region. + +--- + +## External Endpoints + +External endpoints connect Traffic Manager to resources outside Azure — on-premises data centers, other cloud providers, or any internet-accessible service. + +### Addressing Options + +External endpoints can be specified using: +- **FQDN (Fully Qualified Domain Name)**: `onprem.contoso.com`, `app.aws.example.com` +- **IPv4 address**: `203.0.113.50` +- **IPv6 address**: `2001:db8::1` + +```bash +# FQDN-based external endpoint +az network traffic-manager endpoint create -g MyRG --profile-name MyTMProfile \ + -n OnPremEndpoint --type externalEndpoints \ + --target onprem.contoso.com --priority 2 + +# IP-based external endpoint (required for MultiValue routing) +az network traffic-manager endpoint create -g MyRG --profile-name MyTMProfile \ + -n AWSEndpoint --type externalEndpoints \ + --target 52.10.20.30 --priority 3 +``` + +### Health Probe Requirements + +External endpoint health probes are sent from Azure data centers. The external endpoint MUST: + +1. **Be accessible from the internet** — Traffic Manager probes cannot reach private/internal IPs. +2. **Accept connections on the configured probe port** (typically 80 or 443). +3. **Return an HTTP 200 status** at the configured probe path (for HTTP/HTTPS probes). +4. **Allow Traffic Manager probe source IPs** through any firewalls. Probe IPs are published as the `AzureTrafficManager` service tag. + +If the external endpoint is behind a firewall, add rules to permit inbound connections from the [Traffic Manager probe IP ranges](https://www.microsoft.com/download/details.aspx?id=56519). + +### Use Cases for External Endpoints + +| Scenario | Configuration | +|----------|--------------| +| Multi-cloud (Azure + AWS) | Azure endpoint priority 1, external AWS endpoint priority 2 | +| On-premises failover | Azure endpoint priority 1, on-prem FQDN priority 2 | +| Hybrid deployment | Performance routing with Azure and on-prem endpoints in different locations | +| Third-party CDN | External endpoint pointing to CDN origin | + +### Explicit Location Required for Performance Routing + +External endpoints do not have Azure region metadata. When using Performance routing, you MUST set `--endpoint-location`: + +```bash +az network traffic-manager endpoint create -g MyRG --profile-name PerfProfile \ + -n OnPremDC --type externalEndpoints \ + --target dc.contoso.com --endpoint-location "East US" +``` + +The location should represent the Azure region closest to the external resource. + +--- + +## Nested Endpoints + +Nested endpoints use another Traffic Manager profile as the target. This enables combining different routing methods at multiple levels — for example, geographic routing at the outer level with priority failover at the inner level. + +### When to Use Nested Profiles + +- **Combining routing methods**: Performance routing globally, weighted routing within each region. +- **Geographic failover**: Geographic routing at the outer level prevents failover to other regions, but an inner Priority profile provides failover within the assigned region. +- **Gradual migration with regional control**: Outer profile routes by region, inner profile handles canary deployments with weighted routing. +- **Large endpoint sets**: A single Traffic Manager profile supports up to 200 endpoints. Nesting allows scaling beyond this limit. + +### MinChildEndpoints Setting + +The `--min-child-endpoints` parameter controls when the outer profile considers the nested endpoint unhealthy: + +- If the number of healthy endpoints in the inner profile drops below `MinChildEndpoints`, the outer profile marks the nested endpoint as Degraded. +- Default value is 1 — the nested endpoint is considered healthy as long as at least one child endpoint is healthy. +- Set higher values when you need a minimum level of capacity before routing to a region. + +```bash +# Nested endpoint requiring at least 2 healthy child endpoints +az network traffic-manager endpoint create -g MyRG --profile-name OuterProfile \ + -n RegionA --type nestedEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/trafficManagerProfiles/InnerProfileA \ + --min-child-endpoints 2 --endpoint-location "East US" +``` + +### Cascading Health Checks + +Health checks cascade from inner to outer profiles: + +1. Inner profile probes individual endpoint health (App Service, VM, etc.). +2. If enough inner endpoints fail (below MinChildEndpoints), the inner profile is considered unhealthy. +3. Outer profile detects this and stops routing to the nested endpoint. +4. Traffic shifts to the next outer endpoint based on the outer profile's routing method. + +### Complex Routing Example + +Three-level nesting: Geographic → Performance → Priority: + +``` +Outer: Geographic routing +├── GEO-EU → Mid: Performance routing (Europe regions) +│ ├── West Europe → Inner: Priority (westeu-primary=1, westeu-secondary=2) +│ └── North Europe → Inner: Priority (northeu-primary=1, northeu-secondary=2) +├── GEO-NA → Mid: Performance routing (North America regions) +│ ├── East US → Inner: Priority (eastus-primary=1, eastus-secondary=2) +│ └── West US → Inner: Priority (westus-primary=1, westus-secondary=2) +└── WORLD → Fallback endpoint +``` + +### Nested Profile Endpoint Location + +For nested endpoints used in Performance routing profiles, you MUST set `--endpoint-location`. This tells the outer profile what region the inner profile represents. + +--- + +## Endpoint Monitoring Status Values + +Traffic Manager assigns a monitoring status to each endpoint based on health probe results: + +| Status | Meaning | Traffic Manager Behavior | +|--------|---------|------------------------| +| **Online** | Endpoint is healthy and passing probes | Included in DNS responses | +| **Degraded** | Endpoint is failing health probes | Excluded from DNS responses (unless all are degraded) | +| **Disabled** | Administrator manually disabled the endpoint | Excluded from DNS responses; no probes sent | +| **Inactive** | Endpoint configuration is incomplete or profile is disabled | Excluded from DNS responses; no probes sent | +| **Stopped** | The underlying Azure resource is stopped (e.g., App Service stopped) | Excluded from DNS responses | +| **CheckingEndpoint** | Traffic Manager just started or endpoint was just re-enabled; probes in progress | May be included in DNS responses during initial check | + +### All-Degraded Fallback + +When ALL endpoints in a profile are Degraded, Traffic Manager returns all endpoints in DNS responses as a best-effort measure rather than returning no results. This prevents a total outage caused by false-positive health check failures. + +--- + +## Endpoint Weight and Priority + +### Weight (for Weighted Routing) + +- Range: 1–1000 +- Default: 1 +- Weight 0 removes the endpoint from DNS responses (drain traffic). +- All weights are relative: endpoints with weight 100 and 200 get 33% and 67% of traffic. + +### Priority (for Priority Routing) + +- Range: 1–1000 +- Lower value = higher precedence (1 is checked first). +- Each endpoint must have a unique priority within the profile. +- If the highest-priority healthy endpoint fails, traffic shifts to the next priority. + +--- + +## Always-Serve Endpoints + +The Always Serve setting overrides health checking for an endpoint. When enabled, Traffic Manager includes the endpoint in DNS responses regardless of its health probe status. + +### When to Use + +- The endpoint has a non-standard health check that Traffic Manager probes cannot reach. +- You are handling health checks at the application level (client-side retry logic). +- During debugging — temporarily include an endpoint that is showing as Degraded. + +```bash +az network traffic-manager endpoint update -g MyRG --profile-name MyTMProfile \ + -n MyEndpoint --type azureEndpoints --always-serve Enabled +``` + +> **Warning**: Using Always Serve bypasses Traffic Manager's health monitoring. If the endpoint is actually down, users will be directed to an unavailable service. + +--- + +## Troubleshooting + +### Endpoint Shows Degraded but Application Is Healthy + +1. **Check the probe path**: Verify the path configured in the Traffic Manager profile returns HTTP 200 on the endpoint. + ```bash + curl -I https://myapp-eastus.azurewebsites.net/health + ``` +2. **Check the probe port**: The port in the profile must match the port the application listens on. +3. **Check HTTPS certificate**: If using HTTPS probes, the endpoint must have a valid TLS certificate trusted by Traffic Manager (no self-signed certs). +4. **Check firewall rules**: Traffic Manager probes come from Azure data center IPs. Ensure the endpoint allows inbound traffic from the `AzureTrafficManager` service tag. +5. **Check App Service tier**: Free and Basic tiers do not support Traffic Manager. Upgrade to Standard or higher. + +### Endpoint Not Receiving Traffic + +1. **Check endpoint status**: Ensure the endpoint is Enabled, not Disabled or Stopped. + ```bash + az network traffic-manager endpoint show -g MyRG --profile-name MyTMProfile \ + -n MyEndpoint --type azureEndpoints --query endpointStatus + ``` +2. **Check DNS resolution**: Verify that the Traffic Manager DNS name resolves to the expected endpoint. + ```bash + nslookup myapp-tm.trafficmanager.net + ``` +3. **Check routing method and configuration**: For Priority routing, verify the endpoint has the correct priority. For Weighted routing, verify the weight is not 0. +4. **Check DNS TTL caching**: DNS resolvers cache responses. After a change, wait for the TTL to expire before testing. +5. **Check profile status**: Ensure the profile itself is Enabled. + ```bash + az network traffic-manager profile show -g MyRG -n MyTMProfile --query profileStatus + ``` diff --git a/plugin/skills/azure-traffic-manager/references/health-checks.md b/plugin/skills/azure-traffic-manager/references/health-checks.md new file mode 100644 index 000000000..4c1c6935d --- /dev/null +++ b/plugin/skills/azure-traffic-manager/references/health-checks.md @@ -0,0 +1,357 @@ +# Traffic Manager Health Checks + +Traffic Manager uses continuous health probing to determine which endpoints are healthy and eligible to receive traffic. Understanding the probe mechanism is critical for configuring reliable failover and minimizing downtime. + +--- + +## Health Probe Mechanism + +Traffic Manager sends periodic health probes to each endpoint from multiple Azure data center locations worldwide. If an endpoint fails to respond correctly within the configured thresholds, Traffic Manager marks it as Degraded and removes it from DNS responses. + +### Probe Flow + +1. Traffic Manager sends a probe request (HTTP GET, HTTPS GET, or TCP connection) to the endpoint. +2. The endpoint must respond within the configured timeout period. +3. For HTTP/HTTPS probes, the response status code must be in the acceptable range (200–299 by default). +4. If the probe fails, Traffic Manager increments a failure counter for that endpoint. +5. After exceeding the tolerated failure count, Traffic Manager marks the endpoint as Degraded. +6. Once Degraded, the endpoint is removed from DNS responses. +7. Traffic Manager continues probing the Degraded endpoint. When it passes probes again, it transitions back to Online. + +--- + +## Protocol Options + +### HTTP Probes + +- Traffic Manager sends an HTTP GET request to the configured path and port. +- Expects an HTTP 200–299 status code by default. +- Use for standard web applications that do not require encryption. +- Probe path example: `/health` or `/api/healthcheck`. + +### HTTPS Probes + +- Identical to HTTP probes but over TLS/SSL. +- The endpoint must present a valid TLS certificate from a trusted CA — self-signed certificates will cause probe failures. +- Traffic Manager does NOT validate the certificate's hostname against the endpoint FQDN; it only validates the trust chain. +- **Recommended for production** — most web applications should use HTTPS probes. + +### TCP Probes + +- Traffic Manager attempts a TCP connection to the configured port. +- Success = TCP connection established (SYN-ACK received). +- Use for non-HTTP services: databases, message queues, custom TCP services. +- TCP probes do not check response content — only connectivity. + +```bash +# HTTP probe +az network traffic-manager profile create -g MyRG -n HttpProfile \ + --routing-method Priority --unique-dns-name myapp-http \ + --protocol HTTP --port 80 --path /health + +# HTTPS probe (recommended) +az network traffic-manager profile create -g MyRG -n HttpsProfile \ + --routing-method Priority --unique-dns-name myapp-https \ + --protocol HTTPS --port 443 --path /health + +# TCP probe (non-HTTP services) +az network traffic-manager profile create -g MyRG -n TcpProfile \ + --routing-method Priority --unique-dns-name myapp-tcp \ + --protocol TCP --port 3306 +``` + +--- + +## Custom Headers + +You can add custom HTTP headers to health probe requests. This is essential for endpoints that use host-based routing or require specific headers for health checks. + +### Host Header for Shared Hosting + +If multiple applications share the same IP address (e.g., App Service with custom domains), the health probe must include the correct Host header: + +```bash +az network traffic-manager profile update -g MyRG -n MyTMProfile \ + --custom-headers host=myapp.contoso.com +``` + +### Multiple Custom Headers + +Add multiple headers for specialized health check endpoints: + +```bash +az network traffic-manager profile update -g MyRG -n MyTMProfile \ + --custom-headers host=myapp.contoso.com x-health-check=traffic-manager +``` + +### Common Custom Header Use Cases + +| Header | Purpose | +|--------|---------| +| `Host: myapp.contoso.com` | Target specific app on shared hosting | +| `X-Health-Check: traffic-manager` | Identify probe source in application logs | +| `Authorization: Bearer ` | Authenticate probes to protected health endpoints | + +--- + +## Expected Status Codes + +By default, Traffic Manager considers HTTP 200–299 as healthy. You can configure custom acceptable status code ranges: + +```bash +az network traffic-manager profile update -g MyRG -n MyTMProfile \ + --status-code-ranges 200-299 301 +``` + +### Common Configurations + +| Scenario | Status Codes | Reason | +|----------|-------------|--------| +| Standard web app | 200–299 (default) | Normal healthy response | +| App with redirects | 200–299, 301, 302 | Health path redirects to login page | +| API with custom codes | 200 | Strict — only explicit 200 is healthy | + +> **Note**: 3xx redirects are NOT followed by Traffic Manager. If the health path returns a 301 redirect, you must either include 301 in acceptable codes or change the health path to return 200 directly. + +--- + +## Probe Path Design + +The health probe path (`--path`) determines what URL Traffic Manager checks. A well-designed health endpoint goes beyond "is the web server running" and verifies the application is actually functional. + +### Recommended Health Check Layers + +``` +Level 1 — Shallow: /health → Returns 200 if the process is running +Level 2 — Medium: /health → Checks database connectivity, returns 200 if connected +Level 3 — Deep: /health → Checks database + downstream APIs + cache, returns 200 if all healthy +``` + +### Guidelines + +- **Use a dedicated health endpoint** — don't use `/` (home page) because it may return 200 even when backend services are down. +- **Check critical dependencies** — if the app needs a database, the health check should verify database connectivity. +- **Don't check non-critical dependencies** — a slow logging service should not make the health check fail. +- **Return quickly** — the health endpoint should respond within 2–3 seconds. Long-running checks may cause timeouts. +- **Use GET, not POST** — Traffic Manager sends HTTP GET requests for health probes. +- **Return appropriate status codes** — 200 for healthy, 503 for unhealthy. + +### Example Health Endpoint (ASP.NET) + +```csharp +app.MapGet("/health", async (DbContext db) => +{ + try + { + await db.Database.CanConnectAsync(); + return Results.Ok(new { status = "healthy" }); + } + catch + { + return Results.StatusCode(503); + } +}); +``` + +--- + +## Failover Timing + +Failover time is the total elapsed time from when an endpoint goes down to when DNS queries are routed to a different endpoint. + +### Calculation Formula + +``` +Failover time = (Probe interval × Tolerated failures) + Probe interval + DNS TTL + +Components: + - Detection: Probe interval × Tolerated failures (time to confirm failure) + - Propagation: Probe interval (next probe cycle to update) + - DNS cache: DNS TTL (clients cache old DNS response) +``` + +### Configuration Scenarios + +| Config | Interval | Failures | TTL | Failover Time | +|--------|----------|----------|-----|---------------| +| Default | 30s | 3 | 60s | (30×3)+30+60 = **180s (3 min)** | +| Fast | 10s | 1 | 30s | (10×1)+10+30 = **50s** | +| Aggressive | 10s | 0 | 10s | (10×0)+10+10 = **20s** | +| Conservative | 30s | 5 | 120s | (30×5)+30+120 = **300s (5 min)** | + +### Fast Failover Configuration + +For the fastest failover (with increased probe cost): + +```bash +az network traffic-manager profile update -g MyRG -n MyTMProfile \ + --interval 10 --timeout 5 --tolerated-failures 1 +az network traffic-manager profile update -g MyRG -n MyTMProfile --ttl 30 +``` + +> **Trade-off**: Faster detection means more probe traffic and more DNS queries. The 10s interval with 0 tolerated failures is aggressive and may cause false positives from transient issues. + +--- + +## Endpoint Monitor Status State Machine + +Endpoints transition through states based on health probe results: + +``` + ┌─────────────────┐ + │ CheckingEndpoint │ ← Initial state / re-enabled + └────────┬────────┘ + │ + ┌────────────┼────────────┐ + ▼ │ ▼ + ┌──────────┐ │ ┌────────────┐ + │ Online │◄─────┘ │ Degraded │ + └────┬─────┘ └─────┬──────┘ + │ │ + │ Probe failures │ Probe successes + │ exceed threshold │ resume + │ │ + └───────►Degraded────────┘ + │ + ┌────┴────┐ + │ Stopped │ ← Resource stopped (App Service, etc.) + └─────────┘ +``` + +### State Descriptions + +| State | Probes Sent? | In DNS? | Trigger | +|-------|-------------|---------|---------| +| CheckingEndpoint | Yes | Sometimes | Endpoint just created, re-enabled, or profile restarted | +| Online | Yes | Yes | Probes succeeding within thresholds | +| Degraded | Yes | No (unless all degraded) | Probe failures exceed tolerated count | +| Disabled | No | No | Admin manually disabled | +| Stopped | No | No | Underlying Azure resource is stopped | +| Inactive | No | No | Profile disabled or endpoint misconfigured | + +--- + +## Cascading Failures in Nested Profiles + +In nested profile configurations, health status cascades from inner to outer profiles: + +1. **Inner profile** probes each child endpoint directly. +2. If healthy child endpoints drop below `MinChildEndpoints`, the inner profile is considered unhealthy. +3. **Outer profile** detects the nested endpoint as Degraded and stops routing traffic to it. +4. Traffic shifts to the next outer-level endpoint. + +### Example Cascade + +``` +Outer Profile (Priority routing) +├── Priority 1: Inner Profile East US (MinChildEndpoints=2) +│ ├── App Service A — Online ✓ +│ ├── App Service B — Degraded ✗ +│ └── App Service C — Degraded ✗ +│ → Only 1 healthy (A) < MinChildEndpoints (2) → Outer marks East US as Degraded +├── Priority 2: Inner Profile West US (MinChildEndpoints=1) +│ ├── App Service D — Online ✓ +│ └── App Service E — Online ✓ +│ → 2 healthy ≥ MinChildEndpoints (1) → Outer routes traffic here +``` + +--- + +## Health Check Source IPs + +Traffic Manager probes originate from Azure data center IP addresses. These IPs are published under the **AzureTrafficManager** service tag. + +### Allowing Probe Traffic Through Firewalls + +```bash +# Download the service tag list to find Traffic Manager IP ranges +az network list-service-tags --location eastus --query "values[?name=='AzureTrafficManager'].properties.addressPrefixes" -o tsv +``` + +Add these IP ranges to your firewall allow list for the configured probe port. + +--- + +## Troubleshooting + +### Endpoint Shows Degraded but Application Is Healthy + +**Cause 1: Probe path returns non-200 status code** +```bash +# Test what the probe path returns +curl -v https://myapp.azurewebsites.net/health +# Verify status code is 200-299 +``` + +**Cause 2: Wrong port or protocol configured** +```bash +# Check current profile probe settings +az network traffic-manager profile show -g MyRG -n MyTMProfile \ + --query monitorConfig +``` + +**Cause 3: Probe timeout too short** +If your health endpoint is slow (checking database, downstream services), increase the timeout or optimize the health check. + +**Cause 4: SSL certificate issues** +HTTPS probes require a valid TLS certificate from a trusted CA. Self-signed certificates or expired certificates will fail probes. + +### Firewall Blocking Traffic Manager Probe IPs + +**Symptoms**: Endpoint shows Degraded immediately after creation. Application responds correctly when accessed directly. + +**Fix**: Add Azure Traffic Manager probe IPs to the firewall allow list for the probe port (typically 80 or 443). Use the `AzureTrafficManager` service tag. + +```bash +# NSG rule to allow Traffic Manager probes +az network nsg rule create -g MyRG --nsg-name MyNSG \ + -n AllowTrafficManagerProbes --priority 100 \ + --source-address-prefixes AzureTrafficManager \ + --destination-port-ranges 443 --protocol Tcp --access Allow +``` + +### Probe Path Returning Wrong Status Code + +**Symptoms**: Endpoint alternates between Online and Degraded, or is persistently Degraded. + +**Diagnosis**: +```bash +# Check what status code the probe path returns +curl -s -o /dev/null -w "%{http_code}" https://myapp.azurewebsites.net/health +# If it returns 301, 302, 403, or 500 — that's the problem +``` + +**Fixes**: +- Change the probe path to one that returns 200. +- Add the returned status code to the acceptable range: `--status-code-ranges 200-299 301`. +- Fix the application health endpoint to return 200 when healthy. + +### SSL Certificate Issues with HTTPS Probes + +**Symptoms**: Endpoint shows Degraded. Switching to HTTP probes resolves the issue. + +**Common causes**: +- Self-signed certificate on the endpoint. +- Expired TLS certificate. +- Intermediate CA certificate missing from the chain. +- Certificate issued for a different hostname (Traffic Manager does not enforce hostname matching but the TLS handshake may fail if the server requires SNI). + +**Fix**: Install a valid certificate from a trusted CA (Let's Encrypt, DigiCert, etc.) with a complete certificate chain. + +### Endpoint Flapping Between Online and Degraded + +**Symptoms**: Endpoint status changes frequently, causing intermittent routing changes. + +**Common causes**: +1. **Health endpoint is slow**: Response time occasionally exceeds probe timeout. + - Fix: Optimize health endpoint or increase timeout (max 10s). +2. **Tolerated failures too low**: Single transient failure causes Degraded status. + - Fix: Increase `--tolerated-failures` to 2 or 3. +3. **Intermittent dependency failure**: Health check depends on a flaky downstream service. + - Fix: Remove non-critical dependency checks from the health endpoint. + +```bash +# Increase resilience to transient failures +az network traffic-manager profile update -g MyRG -n MyTMProfile \ + --timeout 10 --tolerated-failures 3 +``` diff --git a/plugin/skills/azure-traffic-manager/references/routing-methods.md b/plugin/skills/azure-traffic-manager/references/routing-methods.md new file mode 100644 index 000000000..fed255c05 --- /dev/null +++ b/plugin/skills/azure-traffic-manager/references/routing-methods.md @@ -0,0 +1,390 @@ +# Traffic Manager Routing Methods + +## Priority Routing + +Priority routing provides active-passive failover. Traffic Manager returns the healthy endpoint with the lowest priority value (1 = highest precedence). If that endpoint goes down, traffic shifts to the next healthy endpoint in priority order. + +### How It Works + +1. Each endpoint is assigned a unique priority value from 1 to 1000. +2. When a DNS query arrives, Traffic Manager returns the endpoint with the lowest priority value that is currently healthy. +3. If the primary endpoint fails its health check, Traffic Manager returns the next-lowest priority value endpoint. +4. When the primary recovers and passes health checks again, Traffic Manager shifts DNS responses back to it. + +### Setup Example + +```bash +# Create the profile +az network traffic-manager profile create -g MyRG -n FailoverProfile \ + --routing-method Priority --unique-dns-name myapp-failover \ + --ttl 30 --protocol HTTPS --port 443 --path /health + +# Primary — East US (priority 1, checked first) +az network traffic-manager endpoint create -g MyRG --profile-name FailoverProfile \ + -n Primary-EastUS --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-eastus \ + --priority 1 + +# Secondary — West US (priority 2, used when primary is down) +az network traffic-manager endpoint create -g MyRG --profile-name FailoverProfile \ + -n Secondary-WestUS --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-westus \ + --priority 2 + +# Tertiary — North Europe (priority 3, last resort) +az network traffic-manager endpoint create -g MyRG --profile-name FailoverProfile \ + -n Tertiary-NorthEU --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-northeu \ + --priority 3 +``` + +### Active-Passive Pattern + +- **Active**: Priority 1 endpoint serves all traffic during normal operation. +- **Passive**: Priority 2+ endpoints remain on standby, receiving health probes but no user traffic. +- **Failover**: When the active endpoint fails, Traffic Manager automatically returns the next healthy endpoint. +- **Failback**: When the active endpoint recovers, Traffic Manager returns to it (no manual intervention). + +### Important Behaviors + +- Each endpoint MUST have a unique priority value within a profile. +- If all endpoints are unhealthy, Traffic Manager returns all endpoints in DNS (degraded mode) as a last resort. +- Failover time depends on probe interval, tolerated failures, and DNS TTL. See the health checks reference for calculation. + +--- + +## Weighted Routing + +Weighted routing distributes traffic across endpoints proportionally based on assigned weight values. Each endpoint receives a share of traffic equal to its weight divided by the total weight of all healthy endpoints. + +### Weight Calculation + +``` +Traffic % to endpoint = (endpoint weight) / (sum of all healthy endpoint weights) × 100 + +Example: Three endpoints with weights 50, 30, 20 + Endpoint A: 50 / (50+30+20) = 50% of traffic + Endpoint B: 30 / 100 = 30% of traffic + Endpoint C: 20 / 100 = 20% of traffic +``` + +### A/B Testing Pattern + +Route a small percentage of traffic to a canary deployment for testing: + +```bash +az network traffic-manager profile create -g MyRG -n ABTestProfile \ + --routing-method Weighted --unique-dns-name myapp-abtest \ + --ttl 30 --protocol HTTPS --port 443 --path /health + +# Production — receives 90% of traffic +az network traffic-manager endpoint create -g MyRG --profile-name ABTestProfile \ + -n Production --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-prod \ + --weight 90 + +# Canary — receives 10% of traffic +az network traffic-manager endpoint create -g MyRG --profile-name ABTestProfile \ + -n Canary --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-canary \ + --weight 10 +``` + +### Gradual Migration (Blue-Green Deployment) + +Shift traffic progressively from old to new deployment: + +1. Start: Old = 100, New = 0 (all traffic to old). +2. Day 1: Old = 90, New = 10. +3. Day 2: Old = 70, New = 30. +4. Day 3: Old = 50, New = 50. +5. Final: Old = 0, New = 100 (all traffic to new). + +```bash +# Update weights over time +az network traffic-manager endpoint update -g MyRG --profile-name MigrationProfile \ + -n OldDeployment --type azureEndpoints --weight 50 +az network traffic-manager endpoint update -g MyRG --profile-name MigrationProfile \ + -n NewDeployment --type azureEndpoints --weight 50 +``` + +### Weight 0 Behavior + +- Setting an endpoint's weight to **0** effectively removes it from DNS responses. +- Traffic Manager does NOT return endpoints with weight 0 unless all other endpoints are also 0 or unhealthy. +- Weight 0 is useful for draining traffic during maintenance without disabling the endpoint. + +### Valid Weight Range + +Weights can be set from 1 to 1000. Equal weights distribute traffic evenly. All endpoints default to weight 1 if not specified. + +--- + +## Performance Routing + +Performance routing sends users to the endpoint with the lowest network latency from the user's location. Traffic Manager maintains an Internet Latency Table that maps IP address ranges to Azure regions. + +### How the Azure Latency Table Works + +1. Traffic Manager continuously measures round-trip latency from every Azure region to IP prefixes across the internet. +2. When a DNS query arrives, Traffic Manager identifies the source IP (the DNS resolver's IP, not the end user's IP). +3. It looks up which Azure region has the lowest latency for that source IP prefix. +4. It returns the healthy endpoint in that region. + +### Regional Endpoint Placement Strategy + +- Deploy endpoints in Azure regions closest to your largest user populations. +- For global reach: East US, West Europe, Southeast Asia cover most major populations. +- Check latency from key user locations using `az network traffic-manager profile show` diagnostics. + +### Setup Example + +```bash +az network traffic-manager profile create -g MyRG -n PerfProfile \ + --routing-method Performance --unique-dns-name myapp-perf \ + --ttl 60 --protocol HTTPS --port 443 --path /health + +# Each endpoint's location is set by the Azure resource's region +az network traffic-manager endpoint create -g MyRG --profile-name PerfProfile \ + -n EastUS --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-eastus + +az network traffic-manager endpoint create -g MyRG --profile-name PerfProfile \ + -n WestEurope --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-westeu + +# External endpoints require explicit location +az network traffic-manager endpoint create -g MyRG --profile-name PerfProfile \ + -n AsiaDC --type externalEndpoints \ + --target asia.contoso.com --endpoint-location "Southeast Asia" +``` + +### Limitations + +- Latency is measured from the DNS resolver, not the end user. Users behind centralized DNS resolvers (e.g., Google 8.8.8.8) may not get the closest endpoint. +- External endpoints must have `--endpoint-location` explicitly set because Traffic Manager cannot infer their Azure region. +- The latency table is periodically updated — it does not react to transient network congestion in real time. + +--- + +## Geographic Routing + +Geographic routing directs users to specific endpoints based on the geographic origin of their DNS query. This is used for data residency, compliance, and localized content delivery. + +### Geographic Hierarchy + +Traffic Manager uses a four-level hierarchy for mapping: + +``` +World (all regions — catch-all) +├── GEO-AF Africa +├── GEO-AN Antarctica +├── GEO-AS Asia +├── GEO-EU Europe +│ ├── FR France +│ │ ├── FR-A Alsace +│ │ └── ... +│ ├── DE Germany +│ └── ... +├── GEO-ME Middle East +├── GEO-NA North America +│ ├── US United States +│ │ ├── US-CA California +│ │ └── ... +│ └── CA Canada +├── GEO-SA South America +└── GEO-OC Oceania +``` + +### Assignment Rules + +- Each geographic region can be assigned to ONLY ONE endpoint within a profile. +- An endpoint can have multiple regions assigned to it. +- If a region is not assigned to any endpoint, Traffic Manager returns NXDOMAIN (no answer) for queries from that region. +- Always assign "World" to a fallback endpoint to avoid unanswered queries. + +### Setup Example + +```bash +az network traffic-manager profile create -g MyRG -n GeoProfile \ + --routing-method Geographic --unique-dns-name myapp-geo \ + --ttl 60 --protocol HTTPS --port 443 --path /health + +# Europe endpoint — serves all European queries +az network traffic-manager endpoint create -g MyRG --profile-name GeoProfile \ + -n EuropeEndpoint --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-eu \ + --geo-mapping "GEO-EU" + +# US endpoint — serves United States queries +az network traffic-manager endpoint create -g MyRG --profile-name GeoProfile \ + -n USEndpoint --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-us \ + --geo-mapping "US" + +# Catch-all endpoint — serves everything not explicitly mapped +az network traffic-manager endpoint create -g MyRG --profile-name GeoProfile \ + -n DefaultEndpoint --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-default \ + --geo-mapping "WORLD" +``` + +### Fallback Behavior + +- If the assigned endpoint is unhealthy, Traffic Manager returns NXDOMAIN for that region — it does NOT fall back to another geographic endpoint. +- To avoid this, use nested profiles: outer profile uses Geographic routing, inner profile uses Priority routing for failover within each region. + +--- + +## MultiValue Routing + +MultiValue routing returns multiple healthy endpoint IP addresses in a single DNS response. The client's DNS resolver or application can then choose among them, enabling client-side failover without waiting for DNS TTL expiry. + +### When to Use + +- Your clients support trying multiple IPs on connection failure (most HTTP clients and browsers do this). +- You want faster failover than DNS TTL-based switching provides. +- You need to expose multiple healthy endpoints simultaneously. + +### MaxReturn Setting + +- `MaxReturn` controls how many IP addresses are returned per DNS query (range: 1–8, but practically up to the number of healthy endpoints). +- Only healthy endpoints are included in the response. + +```bash +az network traffic-manager profile create -g MyRG -n MultiProfile \ + --routing-method MultiValue --unique-dns-name myapp-multi \ + --ttl 60 --protocol HTTPS --port 443 --path /health \ + --max-return 3 +``` + +### Behavior + +- All healthy endpoints are candidates; Traffic Manager selects up to MaxReturn of them. +- Endpoints MUST have IPv4 or IPv6 addresses (not FQDNs) — only external endpoints with IP addresses or Azure endpoints with Public IP resources work. +- If fewer healthy endpoints exist than MaxReturn, all healthy endpoints are returned. + +--- + +## Subnet Routing + +Subnet routing maps specific client IP address ranges (or subnets) to designated endpoints. When a DNS query arrives, Traffic Manager checks the source IP against configured subnet mappings and returns the matched endpoint. + +### Use Cases + +- **Enterprise routing**: Route internal corporate users (by known IP ranges) to a specific deployment. +- **ISP-specific routing**: Direct users from specific ISPs to optimized endpoints. +- **Regional override**: Override performance routing for known IP ranges that perform better on a different endpoint. + +### Setup Example + +```bash +az network traffic-manager profile create -g MyRG -n SubnetProfile \ + --routing-method Subnet --unique-dns-name myapp-subnet \ + --ttl 60 --protocol HTTPS --port 443 --path /health + +# Route corporate office traffic to internal endpoint +az network traffic-manager endpoint create -g MyRG --profile-name SubnetProfile \ + -n CorpEndpoint --type externalEndpoints \ + --target corp.contoso.com \ + --subnets 10.0.0.0:24 203.0.113.0:28 + +# Route all other traffic to public endpoint +az network traffic-manager endpoint create -g MyRG --profile-name SubnetProfile \ + -n PublicEndpoint --type externalEndpoints \ + --target public.contoso.com \ + --subnets 0.0.0.0:0 +``` + +### Fallback Behavior + +- If the source IP does not match any configured subnet, Traffic Manager returns the endpoint with the default/fallback subnet (0.0.0.0/0 or ::/0) if one exists. +- If no fallback is configured and no subnet matches, Traffic Manager returns NXDOMAIN. + +--- + +## Routing Method Decision Tree + +Use this guide to select the appropriate routing method: + +``` +Do you need active-passive failover? + └── YES → Priority routing + +Do you need to split traffic by percentage (A/B test, migration)? + └── YES → Weighted routing + +Do you need users routed to the lowest-latency endpoint? + └── YES → Performance routing + +Do you need to enforce data residency or geographic compliance? + └── YES → Geographic routing + +Do you need clients to receive multiple IPs for client-side failover? + └── YES → MultiValue routing + +Do you need to route based on the client's IP address or subnet? + └── YES → Subnet routing + +Not sure? + └── Start with Performance routing (most common for global apps) +``` + +--- + +## Combining Methods with Nested Profiles + +When a single routing method is insufficient, nest one Traffic Manager profile inside another. The outer profile determines the first-level routing decision; the inner profile refines it. + +### Example: Performance (Outer) + Weighted (Inner) + +Route users to the nearest region (performance), then distribute within that region using weighted routing for canary deployments. + +``` +Outer Profile (Performance routing) +├── East US → Inner Profile A (Weighted: prod=90, canary=10) +├── West Europe → Inner Profile B (Weighted: prod=80, canary=20) +└── Southeast Asia → Inner Profile C (Weighted: prod=100, canary=0) +``` + +```bash +# Create inner profile for East US +az network traffic-manager profile create -g MyRG -n EastUS-Inner \ + --routing-method Weighted --unique-dns-name eastus-inner \ + --ttl 30 --protocol HTTPS --port 443 --path /health + +az network traffic-manager endpoint create -g MyRG --profile-name EastUS-Inner \ + -n Prod --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/prod-eastus \ + --weight 90 + +az network traffic-manager endpoint create -g MyRG --profile-name EastUS-Inner \ + -n Canary --type azureEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/canary-eastus \ + --weight 10 + +# Add inner profile as nested endpoint in outer profile +az network traffic-manager endpoint create -g MyRG --profile-name Outer-Perf \ + -n EastUS --type nestedEndpoints \ + --target-resource-id /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/trafficManagerProfiles/EastUS-Inner \ + --min-child-endpoints 1 --endpoint-location "East US" +``` + +### Example: Geographic (Outer) + Priority (Inner) + +Route users to a regional group (geographic), then use priority routing for failover within each region. + +``` +Outer Profile (Geographic routing) +├── GEO-EU → Inner Profile EU (Priority: westeu=1, northeu=2) +├── GEO-NA → Inner Profile NA (Priority: eastus=1, westus=2) +└── WORLD → Inner Profile Default (Priority: eastus=1, westeu=2) +``` + +This pattern prevents Geographic routing's limitation of returning NXDOMAIN when an endpoint is unhealthy — the inner Priority profile provides automatic failover within the geographic group. + +### Nesting Rules + +- Maximum nesting depth is 10 levels (practical limit: 2–3 levels). +- The `--min-child-endpoints` setting on the nested endpoint controls when the outer profile considers the inner profile unhealthy. If healthy child endpoints drop below this threshold, the outer profile marks the nested endpoint as degraded. +- Nested profiles can use any combination of routing methods at each level. diff --git a/plugin/skills/azure-virtual-network/SKILL.md b/plugin/skills/azure-virtual-network/SKILL.md new file mode 100644 index 000000000..31ec3f626 --- /dev/null +++ b/plugin/skills/azure-virtual-network/SKILL.md @@ -0,0 +1,128 @@ +--- +name: azure-virtual-network +description: "Design, deploy, and manage Azure Virtual Networks including subnets, peering, NSGs, ASGs, service endpoints, UDRs, VNet encryption, and IP addressing. WHEN: create vnet, virtual network, subnet, peering, NSG, network security group, ASG, service endpoint, UDR, route table, IP address, public IP, VNet encryption. DO NOT USE FOR: private endpoints (use azure-private-link), DNS zones (use azure-dns), VPN or ExpressRoute connectivity (use azure-vpn-gateway or azure-expressroute)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Virtual Network Skill + +## When to Use This Skill + +- User wants to create, modify, or troubleshoot an Azure Virtual Network or subnet +- User needs to configure VNet peering (regional or global) +- User asks about Network Security Groups (NSGs) or Application Security Groups (ASGs) +- User needs to set up User-Defined Routes (UDRs) or route tables +- User wants to manage public or private IP addresses, IP prefixes +- User asks about service endpoints for PaaS services +- User needs to enable VNet encryption +- User asks about VNet address space planning or subnet design +- User wants to understand network traffic flow or filtering + +## Rules + +1. Always recommend Standard SKU public IPs for new deployments — Basic SKU is retiring. +2. Reserve at least 5 addresses per subnet (Azure reserves the first 4 and last 1 in each subnet). +3. Never overlap address spaces between VNets that need to peer. +4. Always pair NSG rules with a justification — deny-all-inbound is the default implicit rule. +5. Recommend service endpoints only when private endpoints are not available or not suitable — private endpoints are preferred for new designs. +6. When creating peering, remind users that peering must be created in BOTH directions. +7. For UDRs, always confirm the next hop type and IP before applying — incorrect routes cause outages. +8. Subnet delegation locks a subnet to a single service — do not mix delegated and non-delegated resources. +9. VNet encryption requires supported VM SKUs (Accelerated Networking capable) and is region-specific. +10. Always validate that address space changes won't break existing peerings or connected resources. + +## Services + +| Service | Use When | MCP Tools | CLI | +|---------|----------|-----------|-----| +| Virtual Network | Creating or managing VNets and subnets | `azure__network` → `vnet_list`, `vnet_get` | `az network vnet create/update/show/list` | +| Network Security Group | Filtering network traffic with security rules | `azure__network` → `nsg_list`, `nsg_get` | `az network nsg create/show/list`, `az network nsg rule create` | +| VNet Peering | Connecting VNets within or across regions | — | `az network vnet peering create/show/list` | +| Public IP Address | Assigning public connectivity to resources | `azure__network` → `public_ip_list` | `az network public-ip create/show/list` | +| Route Table / UDR | Controlling traffic routing in subnets | `azure__network` → `route_table_list` | `az network route-table create`, `az network route-table route create` | +| Service Endpoints | Securing PaaS service access from VNet | — | `az network vnet subnet update --service-endpoints` | +| Application Security Group | Grouping VMs for NSG rules without IPs | — | `az network asg create` | + +## MCP Tools + +| Tool | Command | Purpose | +|------|---------|---------| +| `azure__network` | `vnet_list` | List all VNets in a subscription or resource group | +| `azure__network` | `vnet_get` | Get details of a specific VNet including subnets and peerings | +| `azure__network` | `nsg_list` | List all NSGs in a subscription or resource group | +| `azure__network` | `nsg_get` | Get NSG details including all security rules | +| `azure__network` | `public_ip_list` | List all public IP addresses | +| `azure__network` | `route_table_list` | List all route tables and their routes | + +## CLI Fallback + +```bash +# VNet operations +az network vnet create -g MyRG -n MyVNet --address-prefix 10.0.0.0/16 --subnet-name default --subnet-prefix 10.0.0.0/24 +az network vnet show -g MyRG -n MyVNet +az network vnet list -g MyRG -o table +az network vnet subnet create -g MyRG --vnet-name MyVNet -n AppSubnet --address-prefix 10.0.1.0/24 + +# NSG operations +az network nsg create -g MyRG -n MyNSG +az network nsg rule create -g MyRG --nsg-name MyNSG -n AllowHTTPS --priority 100 \ + --direction Inbound --access Allow --protocol Tcp --destination-port-ranges 443 +az network nsg show -g MyRG -n MyNSG + +# VNet peering +az network vnet peering create -g MyRG -n Peer1to2 --vnet-name VNet1 \ + --remote-vnet /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/VNet2 \ + --allow-vnet-access +az network vnet peering list -g MyRG --vnet-name VNet1 -o table + +# Public IP +az network public-ip create -g MyRG -n MyPublicIP --sku Standard --allocation-method Static +az network public-ip list -g MyRG -o table + +# Route table +az network route-table create -g MyRG -n MyRouteTable +az network route-table route create -g MyRG --route-table-name MyRouteTable -n ToFirewall \ + --address-prefix 0.0.0.0/0 --next-hop-type VirtualAppliance --next-hop-ip-address 10.0.2.4 + +# Application Security Group +az network asg create -g MyRG -n WebServers +``` + +## Key Concepts + +### Address Space Planning + +| VNet Size | CIDR | Hosts | Typical Use | +|-----------|------|-------|-------------| +| Small | /24 | 251 | Dev/test, single workload | +| Medium | /20 | 4,091 | Departmental, few subnets | +| Large | /16 | 65,531 | Hub VNet, many subnets | +| Extra Large | /12 | 1,048,571 | Enterprise hub with growth | + +### Azure Reserved Addresses (per subnet) + +| Address | Purpose | +|---------|---------| +| x.x.x.0 | Network address | +| x.x.x.1 | Default gateway | +| x.x.x.2-3 | Azure DNS mapping | +| x.x.x.255 | Broadcast (last address) | + +### NSG Rule Processing Order + +1. Inbound: NSG on subnet → NSG on NIC (lowest priority number wins) +2. Outbound: NSG on NIC → NSG on subnet (lowest priority number wins) +3. Lower priority number = higher precedence (100 beats 200) +4. Default rules exist at priority 65000-65500 (cannot delete, can override) + +## References + +- [VNet Fundamentals](references/vnet-fundamentals.md) +- [NSG Rules Guide](references/nsg-rules.md) +- [Peering Guide](references/peering-guide.md) +- [IP Addressing](references/ip-addressing.md) +- [UDR Guide](references/udr-guide.md) +- [Service Endpoints](references/service-endpoints.md) diff --git a/plugin/skills/azure-virtual-network/references/ip-addressing.md b/plugin/skills/azure-virtual-network/references/ip-addressing.md new file mode 100644 index 000000000..acdf7eef9 --- /dev/null +++ b/plugin/skills/azure-virtual-network/references/ip-addressing.md @@ -0,0 +1,276 @@ +# IP Addressing in Azure Virtual Networks + +## Public IP Address SKUs + +Azure offers two SKUs for public IP addresses: Basic and Standard. + +### Basic vs Standard Comparison + +| Feature | Basic SKU | Standard SKU | +|---------|-----------|-------------| +| Allocation | Static or Dynamic | Static only | +| Availability zones | Not zone-redundant | Zone-redundant by default | +| Routing preference | Internet routing only | Internet or Microsoft network routing | +| Security | Open by default (NSG optional) | Secure by default (NSG required) | +| Load balancer compatibility | Basic LB only | Standard LB only | +| Cross-region LB | Not supported | Supported | +| NAT Gateway | Not supported | Supported | +| Global tier | Not available | Available (for cross-region LB) | +| Idle timeout | 4 min (fixed) | 4–30 min (configurable) | +| Price | Free (for most uses) | Charged per hour + data | + +### Basic SKU Retirement + +> **IMPORTANT**: Basic SKU public IPs are scheduled for retirement on **September 30, 2025**. All new deployments should use Standard SKU. + +#### Migration Steps + +```bash +# Step 1: Check existing Basic SKU public IPs +az network public-ip list --query "[?sku.name=='Basic']" -o table + +# Step 2: Upgrade to Standard (requires disassociation first) +# Disassociate the IP from the resource +az network nic ip-config update -g MyRG --nic-name MyNIC -n ipconfig1 --remove publicIpAddress + +# Upgrade the SKU +az network public-ip update -g MyRG -n MyPublicIP --sku Standard + +# Re-associate (may require NSG rule since Standard is secure-by-default) +az network nic ip-config update -g MyRG --nic-name MyNIC -n ipconfig1 \ + --public-ip-address MyPublicIP +``` + +> **Note**: Migration requires downtime because the public IP must be disassociated, upgraded, and re-associated. Plan for a maintenance window. + +## Static vs Dynamic Allocation + +### Static IP Addresses + +- The IP is assigned immediately when the public IP resource is created. +- The IP does not change across resource stop/start, reboot, or reassignment. +- Use for: DNS records, firewall allowlists, SSL certificates bound to an IP, applications that require a fixed IP. + +### Dynamic IP Addresses (Basic SKU only) + +- The IP is assigned when the public IP is attached to a running resource. +- The IP may change when the resource is stopped and restarted (deallocated). +- Standard SKU does not support dynamic allocation — all Standard IPs are static. + +```bash +# Create a static Standard public IP +az network public-ip create -g MyRG -n MyStaticIP \ + --sku Standard \ + --allocation-method Static \ + --zone 1 2 3 + +# Create a zone-redundant Standard public IP (default behavior) +az network public-ip create -g MyRG -n MyZoneRedundantIP \ + --sku Standard \ + --allocation-method Static +``` + +## IP Prefixes + +### Public IP Prefixes + +A public IP prefix is a contiguous range of public IP addresses that you reserve. This guarantees that your public IPs come from a known range, simplifying firewall rules and allowlisting. + +| Prefix Size | Addresses | Common Use | +|-------------|-----------|------------| +| /31 | 2 | Minimal, small services | +| /30 | 4 | Small deployments | +| /29 | 8 | Medium deployments | +| /28 | 16 | Large deployments, NAT Gateway | + +```bash +# Create a public IP prefix (/28 = 16 addresses) +az network public-ip prefix create -g MyRG -n MyIPPrefix \ + --length 28 \ + --location eastus + +# Create a public IP from the prefix +az network public-ip create -g MyRG -n MyIP1 \ + --sku Standard \ + --public-ip-prefix MyIPPrefix +``` + +### Custom IP Prefixes (BYOIP) + +Bring Your Own IP (BYOIP) lets you use your organization's own public IP ranges in Azure. The range must be validated through the Regional Internet Registry (RIR) and provisioned in Azure before use. + +Requirements: +- Minimum prefix size: /24 (IPv4) or /48 (IPv6) +- ROA (Route Origin Authorization) must be created at your RIR +- Provisioning can take several weeks for validation + +```bash +# Create a custom IP prefix (after RIR validation) +az network custom-ip prefix create -g MyRG -n MyBYOIP \ + --cidr 203.0.113.0/24 \ + --location eastus \ + --authorization-message "" \ + --signed-message "" +``` + +## IPv6 Dual-Stack Support + +Azure VNets support dual-stack (IPv4 + IPv6) networking. VMs can have both IPv4 and IPv6 addresses and communicate over both protocols. + +### Dual-Stack Configuration + +```bash +# Create a dual-stack VNet +az network vnet create -g MyRG -n DualStackVNet \ + --address-prefixes 10.0.0.0/16 fd00:db8::/48 \ + --subnet-name Default \ + --subnet-prefixes 10.0.0.0/24 fd00:db8::/64 + +# Create an IPv6 public IP +az network public-ip create -g MyRG -n MyIPv6PublicIP \ + --sku Standard \ + --allocation-method Static \ + --version IPv6 + +# Create a dual-stack NIC +az network nic create -g MyRG -n DualStackNIC \ + --vnet-name DualStackVNet --subnet Default \ + --private-ip-address-version IPv4 \ + --public-ip-address MyIPv4PublicIP + +# Add IPv6 configuration to the NIC +az network nic ip-config create -g MyRG --nic-name DualStackNIC \ + -n IPv6Config \ + --private-ip-address-version IPv6 \ + --vnet-name DualStackVNet --subnet Default \ + --public-ip-address MyIPv6PublicIP +``` + +### IPv6 Limitations in Azure + +- IPv6-only VNets are not supported — dual-stack is required (IPv4 is always present). +- Not all Azure services support IPv6. Check service documentation before planning. +- IPv6 subnet size must be exactly /64 (Azure requirement). +- IPv6 UDRs are supported but some next-hop types are limited. +- NSGs fully support IPv6 source and destination rules. + +## Private IP Addresses + +### Static vs Dynamic Private IPs + +| Type | Behavior | Use When | +|------|----------|----------| +| Dynamic | Azure assigns the next available IP from the subnet | Default — suitable for most workloads | +| Static | You specify the exact IP address | DNS servers, domain controllers, load balancer backends, apps with IP-based dependencies | + +```bash +# Create a NIC with a static private IP +az network nic create -g MyRG -n MyNIC \ + --vnet-name MyVNet --subnet AppSubnet \ + --private-ip-address 10.0.1.10 + +# Update an existing NIC to use a static IP +az network nic ip-config update -g MyRG --nic-name MyNIC -n ipconfig1 \ + --private-ip-address 10.0.1.10 \ + --private-ip-address-allocation Static +``` + +### Private IP Reservation Best Practices + +1. **Document static IP assignments** — maintain a registry to avoid conflicts. +2. **Reserve IPs for infrastructure** — DNS servers, Active Directory, NVAs. +3. **Leave the first few IPs in each subnet unassigned** — Azure uses x.x.x.1 through x.x.x.3. +4. **Start static assignments from x.x.x.10** or higher to leave room for growth. +5. **Use Azure IPAM** or a third-party IPAM tool for large environments. + +## NAT Gateway and Public IPs + +Azure NAT Gateway provides outbound internet connectivity for resources in a subnet using one or more public IP addresses or a public IP prefix. + +### Key Benefits + +- Predictable outbound IPs (from the NAT Gateway's public IP or prefix) +- No SNAT port exhaustion — NAT Gateway supports up to 64,000 SNAT ports per public IP +- Up to 16 public IPs per NAT Gateway (1,024,000 total SNAT ports) +- Replaces the need for public IPs on individual VMs for outbound access + +```bash +# Create a NAT Gateway with a public IP +az network public-ip create -g MyRG -n NatGwIP --sku Standard +az network nat gateway create -g MyRG -n MyNatGw \ + --public-ip-addresses NatGwIP \ + --idle-timeout 10 + +# Associate with a subnet +az network vnet subnet update -g MyRG --vnet-name MyVNet -n AppSubnet \ + --nat-gateway MyNatGw +``` + +### NAT Gateway vs Other Outbound Methods + +| Method | Predictable IP | Port Limits | Cost | +|--------|---------------|-------------|------| +| NAT Gateway | Yes | 64K per IP | Per hour + per GB | +| VM public IP | Yes | 64K per VM | Per hour per IP | +| Load Balancer outbound rules | Yes | Configurable | Part of LB cost | +| Default outbound (retiring) | No | Varies | Free (retiring) | + +> **Note**: Azure default outbound access (where VMs without explicit outbound config get a random public IP) is being retired. Plan to use NAT Gateway, LB outbound rules, or VM public IPs. + +## IP Allocation Strategy for Large Deployments + +### Principles + +1. **Align IP ranges with organizational units** — assign /16 blocks per business unit or environment. +2. **Reserve space for growth** — allocate 2-3x what you currently need. +3. **Use consistent offset patterns** — e.g., subnet .0 is always GatewaySubnet, .1 is AzureBastionSubnet. +4. **Separate production and non-production** — use distinct /8 ranges (e.g., 10.x for prod, 172.16.x for dev). +5. **Plan for hybrid** — coordinate with on-premises teams to avoid overlaps. + +### Example Large-Scale IP Plan + +| Block | CIDR | Purpose | +|-------|------|---------| +| 10.0.0.0/8 | Split into /16 per region | Production Azure | +| 172.16.0.0/12 | Split into /16 per region | Dev/Test Azure | +| 192.168.0.0/16 | On-premises ranges | Corporate network | + +## Troubleshooting + +### Public IP Not Attaching to Resource + +**Symptom**: Error when associating a public IP with a VM NIC or load balancer. +**Causes**: +1. SKU mismatch — Standard IP with Basic LB or vice versa. +2. Zone mismatch — IP zone does not align with the resource's zone. +3. IP already associated with another resource. + +**Fix**: Verify SKU and zone compatibility. Disassociate the IP from any existing resource before attaching to a new one. + +```bash +# Check public IP details and associations +az network public-ip show -g MyRG -n MyPublicIP \ + --query '{sku:sku.name, zone:zones, ipConfig:ipConfiguration.id}' +``` + +### IPv6 Connectivity Issues + +**Symptom**: IPv6 traffic not working despite dual-stack configuration. +**Causes**: +1. NSG does not have IPv6-aware rules. +2. IPv6 subnet not properly configured (/64 required). +3. Application not listening on IPv6 addresses (listening on 0.0.0.0 only covers IPv4; use :: for IPv6). +4. UDRs not configured for IPv6 address prefixes. + +**Fix**: Add explicit IPv6 NSG rules, verify subnet configuration, check application binding. + +### SNAT Port Exhaustion + +**Symptom**: Intermittent outbound connection failures. +**Cause**: Too many outbound connections from VMs sharing a limited number of SNAT ports. +**Fix**: Deploy a NAT Gateway with sufficient public IPs (each IP provides 64K ports). For existing LB-based SNAT, increase the allocated ports per backend instance. + +```bash +# Check SNAT port allocation on a load balancer +az network lb outbound-rule list -g MyRG --lb-name MyLB -o table +``` diff --git a/plugin/skills/azure-virtual-network/references/nsg-rules.md b/plugin/skills/azure-virtual-network/references/nsg-rules.md new file mode 100644 index 000000000..41a2ed69f --- /dev/null +++ b/plugin/skills/azure-virtual-network/references/nsg-rules.md @@ -0,0 +1,285 @@ +# Network Security Groups (NSGs) and Rules + +## Overview + +A Network Security Group (NSG) contains a list of security rules that allow or deny inbound and outbound network traffic to Azure resources. NSGs can be associated with subnets or individual network interfaces (NICs). They act as a stateful firewall — if you allow an inbound request, the response is automatically allowed. + +## Rule Evaluation Order + +NSG rules are evaluated by **priority** — a number between 100 and 4096. Lower numbers are evaluated first and take precedence. + +### Inbound Traffic Flow + +1. Azure evaluates the **subnet-level NSG** first (if one is associated). +2. If traffic passes, Azure evaluates the **NIC-level NSG** (if one is associated). +3. Within each NSG, rules are evaluated from **lowest priority number** (highest precedence) to highest. +4. The first matching rule determines the action (Allow or Deny). No further rules are evaluated. +5. If no custom rule matches, the **default rules** at priority 65000+ apply. + +### Outbound Traffic Flow + +1. Azure evaluates the **NIC-level NSG** first. +2. If traffic passes, Azure evaluates the **subnet-level NSG**. +3. Same priority-based evaluation within each NSG. + +> **Key insight**: For inbound, subnet NSG runs first. For outbound, NIC NSG runs first. Both must allow the traffic for it to flow. + +## Default Rules + +Every NSG is created with three default inbound and three default outbound rules. These cannot be deleted but can be overridden with custom rules at a higher precedence (lower priority number). + +### Default Inbound Rules + +| Priority | Name | Source | Destination | Port | Protocol | Action | +|----------|------|--------|-------------|------|----------|--------| +| 65000 | AllowVnetInBound | VirtualNetwork | VirtualNetwork | Any | Any | Allow | +| 65001 | AllowAzureLoadBalancerInBound | AzureLoadBalancer | Any | Any | Any | Allow | +| 65500 | DenyAllInBound | Any | Any | Any | Any | Deny | + +### Default Outbound Rules + +| Priority | Name | Source | Destination | Port | Protocol | Action | +|----------|------|--------|-------------|------|----------|--------| +| 65000 | AllowVnetOutBound | VirtualNetwork | VirtualNetwork | Any | Any | Allow | +| 65001 | AllowInternetOutBound | Any | Internet | Any | Any | Allow | +| 65500 | DenyAllOutBound | Any | Any | Any | Any | Deny | + +### Service Tags Used in Default Rules + +- **VirtualNetwork**: includes the VNet address space, all connected on-premises address spaces, peered VNets, and VNets connected to a virtual network gateway. +- **AzureLoadBalancer**: represents the Azure infrastructure load balancer health probe source IP (168.63.129.16). +- **Internet**: all addresses outside the VNet address space that are reachable via the public internet. + +## Custom Rule Structure + +Each NSG rule consists of these properties: + +| Property | Description | Values | +|----------|-------------|--------| +| Name | Unique name within the NSG | Up to 80 characters | +| Priority | 100–4096, lower = higher precedence | Leave gaps for insertions | +| Direction | Inbound or Outbound | `Inbound`, `Outbound` | +| Action | Allow or Deny | `Allow`, `Deny` | +| Source | Source IP, CIDR, service tag, or ASG | e.g., `10.0.0.0/24`, `Internet`, `AsgWeb` | +| Source Port | Source port or range | `*`, `80`, `1024-65535` | +| Destination | Destination IP, CIDR, service tag, or ASG | Same options as Source | +| Destination Port | Destination port or range | `*`, `443`, `80,443,8080` | +| Protocol | Network protocol | `Tcp`, `Udp`, `Icmp`, `Esp`, `Ah`, `*` | + +## Application Security Groups (ASGs) + +ASGs let you group VMs by workload function and write NSG rules referencing those groups instead of individual IP addresses. This simplifies rule management significantly in dynamic environments. + +### How ASGs Work + +1. Create an ASG (e.g., `WebServers`, `AppServers`, `DbServers`). +2. Assign VM NICs to the appropriate ASG. +3. Write NSG rules using ASGs as source or destination instead of IP addresses. +4. When VMs are added or removed, ASG membership updates automatically take effect — no rule changes needed. + +### ASG Example: 3-Tier Application + +```bash +# Create ASGs +az network asg create -g MyRG -n WebServers +az network asg create -g MyRG -n AppServers +az network asg create -g MyRG -n DbServers + +# Associate VM NICs with ASGs (during NIC create or update) +az network nic update -g MyRG -n WebVM-NIC --application-security-groups WebServers +az network nic update -g MyRG -n AppVM-NIC --application-security-groups AppServers +az network nic update -g MyRG -n DbVM-NIC --application-security-groups DbServers + +# Create NSG with ASG-based rules +az network nsg create -g MyRG -n TierNSG + +# Allow internet to web tier on 80/443 +az network nsg rule create -g MyRG --nsg-name TierNSG -n AllowWebInbound \ + --priority 100 --direction Inbound --access Allow --protocol Tcp \ + --source-address-prefixes Internet \ + --destination-asgs WebServers \ + --destination-port-ranges 80 443 + +# Allow web tier to app tier on 8080 +az network nsg rule create -g MyRG --nsg-name TierNSG -n AllowWebToApp \ + --priority 200 --direction Inbound --access Allow --protocol Tcp \ + --source-asgs WebServers \ + --destination-asgs AppServers \ + --destination-port-ranges 8080 + +# Allow app tier to DB tier on 1433 +az network nsg rule create -g MyRG --nsg-name TierNSG -n AllowAppToDb \ + --priority 300 --direction Inbound --access Allow --protocol Tcp \ + --source-asgs AppServers \ + --destination-asgs DbServers \ + --destination-port-ranges 1433 +``` + +### ASG Rules and Constraints + +- All NICs assigned to an ASG must be in the **same VNet**. +- You cannot mix ASGs from different VNets in the same rule. +- A single NIC can belong to multiple ASGs (up to the platform limit). +- ASGs are free — no additional cost beyond the NSG. + +## Common Rule Sets + +### Web Tier (Public-Facing) + +| Priority | Name | Dir | Source | Dest | Port | Protocol | Action | +|----------|------|-----|--------|------|------|----------|--------| +| 100 | AllowHTTPS | In | Internet | WebServers | 443 | Tcp | Allow | +| 110 | AllowHTTP | In | Internet | WebServers | 80 | Tcp | Allow | +| 120 | AllowHealthProbe | In | AzureLoadBalancer | WebServers | * | * | Allow | +| 4096 | DenyAllInbound | In | * | * | * | * | Deny | + +### Application Tier (Internal Only) + +| Priority | Name | Dir | Source | Dest | Port | Protocol | Action | +|----------|------|-----|--------|------|------|----------|--------| +| 100 | AllowFromWeb | In | WebServers (ASG) | AppServers | 8080 | Tcp | Allow | +| 110 | AllowHealthProbe | In | AzureLoadBalancer | AppServers | * | * | Allow | +| 4096 | DenyAllInbound | In | * | * | * | * | Deny | + +### Database Tier (Restricted) + +| Priority | Name | Dir | Source | Dest | Port | Protocol | Action | +|----------|------|-----|--------|------|------|----------|--------| +| 100 | AllowSQL | In | AppServers (ASG) | DbServers | 1433 | Tcp | Allow | +| 110 | AllowMySQL | In | AppServers (ASG) | DbServers | 3306 | Tcp | Allow | +| 120 | AllowPostgres | In | AppServers (ASG) | DbServers | 5432 | Tcp | Allow | +| 4096 | DenyAllInbound | In | * | * | * | * | Deny | + +### Management Access (Bastion Only) + +| Priority | Name | Dir | Source | Dest | Port | Protocol | Action | +|----------|------|-----|--------|------|------|----------|--------| +| 100 | AllowSSH | In | AzureBastionSubnet | * | 22 | Tcp | Allow | +| 110 | AllowRDP | In | AzureBastionSubnet | * | 3389 | Tcp | Allow | +| 4096 | DenyAllInbound | In | * | * | * | * | Deny | + +## Priority Numbering Best Practices + +Organize priorities in blocks to keep rules manageable: + +| Range | Purpose | +|-------|---------| +| 100–199 | External/internet-facing rules | +| 200–299 | Inter-tier communication rules | +| 300–399 | Management and monitoring rules | +| 400–499 | Azure service integration rules | +| 500–999 | Reserved for future use | +| 1000–1999 | Application-specific rules | +| 2000–2999 | Security and compliance rules | +| 3000–4096 | Explicit deny rules (before default deny) | + +**Leave gaps** between priorities (e.g., 100, 110, 120 instead of 100, 101, 102) to allow inserting rules later without renumbering. + +## NSG Flow Logs + +NSG flow logs record information about IP traffic flowing through an NSG. They are essential for monitoring, compliance, and troubleshooting. + +### Enabling Flow Logs + +```bash +# Create a storage account for flow logs +az storage account create -g MyRG -n flowlogsstorage --sku Standard_LRS + +# Enable NSG flow logs (version 2 for traffic analytics support) +az network watcher flow-log create \ + --location eastus \ + -g MyRG \ + -n MyFlowLog \ + --nsg MyNSG \ + --storage-account flowlogsstorage \ + --log-version 2 \ + --retention 30 + +# Enable Traffic Analytics (requires Log Analytics workspace) +az network watcher flow-log update \ + --location eastus \ + -g MyRG \ + -n MyFlowLog \ + --traffic-analytics true \ + --workspace MyLogAnalyticsWorkspace +``` + +### Flow Log Data + +Each flow log entry contains: +- Source and destination IP/port +- Protocol +- Traffic direction (inbound/outbound) +- Traffic decision (allowed/denied) +- Byte and packet counts (version 2) +- Flow state (begin, continuing, end — version 2) + +## Diagnostics and Verification + +### Check Effective Security Rules + +The "effective security rules" view shows all NSG rules (from both subnet and NIC NSGs) merged and sorted by priority — exactly what Azure evaluates for traffic decisions. + +```bash +# View effective NSG rules for a NIC +az network nic list-effective-nsg -g MyRG -n MyVM-NIC + +# View effective NSG rules in the portal: +# VM → Networking → Effective security rules +``` + +### IP Flow Verify + +Network Watcher's IP Flow Verify tests whether a specific packet is allowed or denied and identifies which rule made the decision. + +```bash +# Test if inbound TCP/443 is allowed to a specific VM +az network watcher test-ip-flow \ + --direction Inbound \ + --protocol Tcp \ + --local 10.0.0.4:443 \ + --remote 203.0.113.50:12345 \ + --vm MyVM \ + -g MyRG +``` + +## Troubleshooting + +### Rules Not Taking Effect + +**Symptom**: Added an Allow rule but traffic is still blocked. +**Causes**: +1. A higher-precedence (lower number) Deny rule exists above your Allow rule. +2. NSG is not associated with the correct subnet or NIC. +3. Both subnet-level and NIC-level NSGs exist, and one of them is blocking. + +**Fix**: Check effective security rules. Verify NSG associations. Use IP Flow Verify to identify the blocking rule. + +### Traffic Allowed That Should Be Blocked + +**Symptom**: Traffic is flowing despite no explicit Allow rule. +**Causes**: +1. The `AllowVnetInBound` default rule (65000) allows all intra-VNet traffic. +2. The `AllowInternetOutBound` default rule allows all outbound internet traffic. + +**Fix**: Add an explicit Deny rule at a lower priority number than the default rules (any priority under 65000). + +### NSG Associated but No Rules Visible + +**Symptom**: NSG shows as associated to a subnet but the rules panel is empty. +**Cause**: You may be viewing a different NSG, or the rules were deleted. +**Fix**: Verify the NSG resource ID matches the expected one. Default rules always exist even if custom rules are empty. + +### Cannot Create Rule — ASG Error + +**Symptom**: Error when creating a rule with ASG references. +**Cause**: The ASG and the NSG are in different regions, or the ASG is in a different VNet than the NICs. +**Fix**: ASGs must be in the same region as the NSG. All NICs in an ASG must be in the same VNet. + +```bash +# Verify NSG association +az network vnet subnet show -g MyRG --vnet-name MyVNet -n MySubnet --query 'networkSecurityGroup.id' + +# List all rules in an NSG +az network nsg rule list -g MyRG --nsg-name MyNSG -o table --include-default +``` diff --git a/plugin/skills/azure-virtual-network/references/peering-guide.md b/plugin/skills/azure-virtual-network/references/peering-guide.md new file mode 100644 index 000000000..48f43917f --- /dev/null +++ b/plugin/skills/azure-virtual-network/references/peering-guide.md @@ -0,0 +1,238 @@ +# VNet Peering Guide + +## Overview + +VNet peering connects two Azure Virtual Networks, enabling resources in both VNets to communicate using private IP addresses as if they were on the same network. Peered traffic travels over the Microsoft backbone network — it never traverses the public internet. + +Peering must be configured in **both directions** — you create a peering link from VNet A to VNet B, and a second link from VNet B to VNet A. Until both sides are configured, the peering is not functional. + +## Regional vs Global Peering + +| Feature | Regional Peering | Global Peering | +|---------|-----------------|----------------| +| VNet location | Same Azure region | Different Azure regions | +| Latency | Sub-millisecond (same as intra-VNet) | Cross-region latency (varies by distance) | +| Bandwidth | No limit (same region backbone) | Limited by VM SKU egress limits | +| Cost | Ingress and egress charged per GB | Higher per-GB rate than regional | +| Basic Load Balancer | Accessible across peering | **NOT accessible** across global peering | +| Standard Load Balancer | Accessible | Accessible | +| VNet encryption | Supported | Supported (if both VNets enable it) | +| Gateway transit | Supported | Supported | + +> **Important**: If you use Basic Load Balancers, they are not reachable over global peering. Upgrade to Standard SKU for cross-region scenarios. + +## Peering States + +A peering connection goes through the following states: + +| State | Meaning | +|-------|---------| +| **Initiated** | Peering created on one side only. Traffic cannot flow. | +| **Connected** | Peering created on both sides. Traffic flows bidirectionally. | +| **Disconnected** | One side deleted their peering link, or an address space change broke the peering. | + +### State Transitions + +``` +VNet A creates peering → A: Initiated, B: (no peering yet) +VNet B creates peering → A: Connected, B: Connected +VNet A deletes peering → A: (deleted), B: Disconnected +``` + +Once a peering enters the **Disconnected** state, you must delete and recreate the peering on both sides. You cannot recover a disconnected peering by re-adding the link from one side. + +## Transitivity + +**VNet peering is NOT transitive.** If VNet A peers with VNet B, and VNet B peers with VNet C, VNet A cannot reach VNet C through VNet B — unless you explicitly configure routing. + +### Achieving Transitive Routing + +There are several approaches to enable spoke-to-spoke communication: + +#### 1. Hub-Spoke with Azure Firewall or NVA + +The hub VNet runs a firewall or NVA. Spoke VNets have UDRs that send traffic to the hub's firewall IP. The firewall routes traffic between spokes. + +``` +Spoke A → UDR → Hub (Azure Firewall) → UDR → Spoke B +``` + +This is the most common production pattern. It also enables traffic inspection and logging. + +#### 2. Azure Virtual Network Manager (AVNM) + +AVNM supports **connected groups** — a mesh topology where spokes can communicate directly without going through the hub. AVNM automates peering creation and manages routing. + +```bash +# AVNM creates direct connectivity between group members +# No UDRs needed for spoke-to-spoke within a connected group +``` + +#### 3. VPN Gateway in Hub (Route-Based) + +A VPN gateway in the hub with BGP can propagate routes between peered VNets. Less common for intra-Azure traffic but useful in hybrid scenarios. + +## Gateway Transit + +Gateway transit allows a peered VNet to use the other VNet's VPN or ExpressRoute gateway instead of deploying its own. This is common in hub-spoke designs where only the hub has a gateway. + +### Configuration + +On the **hub** (VNet with gateway): +- Enable **"Allow gateway transit"** on the peering link. + +On the **spoke** (VNet without gateway): +- Enable **"Use remote gateways"** on the peering link. + +```bash +# Hub side: allow gateway transit +az network vnet peering update -g HubRG -n HubToSpoke --vnet-name HubVNet \ + --allow-gateway-transit true + +# Spoke side: use remote gateway +az network vnet peering update -g SpokeRG -n SpokeToHub --vnet-name SpokeVNet \ + --use-remote-gateways true +``` + +### Gateway Transit Rules + +1. Only one VNet in the peering pair can have `allow-gateway-transit` enabled. +2. The spoke VNet **must not** have its own gateway to use `use-remote-gateways`. +3. Gateway transit works with both VPN and ExpressRoute gateways. +4. Routes learned by the hub gateway (via BGP) are automatically propagated to the spoke. +5. Gateway transit is supported across global peering. + +## Requirements + +### Address Space + +- Peered VNets **must not** have overlapping address spaces. +- Even partial overlap (e.g., 10.0.0.0/16 and 10.0.1.0/24) is not allowed. +- If you need to change a VNet's address space, you may need to delete peering first, make the change, then recreate peering. + +### Subscriptions and Tenants + +Peering is supported across: +- VNets in the **same subscription** +- VNets in **different subscriptions** (same tenant) +- VNets in **different Azure AD tenants** (cross-tenant peering) + +#### Cross-Subscription Peering Setup + +```bash +# User with access to both subscriptions can create both peering links. +# If different users manage each subscription: + +# Step 1: User A creates peering from VNet A to VNet B +# (User A needs Network Contributor on VNet A and Reader on VNet B) +az network vnet peering create \ + -g RG-A -n AtoB --vnet-name VNetA \ + --remote-vnet /subscriptions/{subB}/resourceGroups/{rgB}/providers/Microsoft.Network/virtualNetworks/VNetB \ + --allow-vnet-access + +# Step 2: User B creates peering from VNet B to VNet A +# (User B needs Network Contributor on VNet B and Reader on VNet A) +az network vnet peering create \ + -g RG-B -n BtoA --vnet-name VNetB \ + --remote-vnet /subscriptions/{subA}/resourceGroups/{rgA}/providers/Microsoft.Network/virtualNetworks/VNetA \ + --allow-vnet-access +``` + +#### Required RBAC Permissions + +| Action | Required Role | +|--------|--------------| +| Create peering on local VNet | Network Contributor (or custom with `Microsoft.Network/virtualNetworks/peer/action`) | +| Read remote VNet | Reader on the remote VNet (or custom with `Microsoft.Network/virtualNetworks/read`) | + +## Peering Options + +When creating a peering link, you can configure these settings: + +| Option | Default | Purpose | +|--------|---------|---------| +| Allow virtual network access | Enabled | Allow traffic to flow between VNets | +| Allow forwarded traffic | Disabled | Allow traffic forwarded by an NVA in the peered VNet | +| Allow gateway transit | Disabled | Let the peered VNet use this VNet's gateway | +| Use remote gateways | Disabled | Use the peered VNet's gateway instead of a local one | + +> **Common mistake**: Forgetting to enable **"Allow forwarded traffic"** when using a hub NVA or firewall. Without it, spoke-to-spoke traffic routed through the hub will be dropped. + +## Limits + +| Resource | Default Limit | Notes | +|----------|--------------|-------| +| Peerings per VNet | 500 | Hard limit — cannot be increased | +| Address spaces per peered VNet | 256 | Across all peered VNets combined | +| Address ranges advertised from hub (with gateway transit) | Varies by gateway SKU | Check VPN Gateway documentation | + +## Troubleshooting + +### Peering Stuck in "Initiated" State + +**Symptom**: Peering shows as "Initiated" and traffic doesn't flow. +**Cause**: The peering link has only been created on one side. +**Fix**: Create the corresponding peering link from the other VNet. Both sides must have a peering link pointing to each other. + +```bash +# Check peering state +az network vnet peering show -g MyRG -n MyPeering --vnet-name MyVNet --query peeringState +``` + +### Traffic Not Flowing Despite "Connected" State + +**Symptom**: Peering is Connected but VMs cannot communicate. +**Causes**: +1. NSG rules blocking traffic between the VNets. +2. UDRs routing traffic away from the peering (e.g., to a firewall that drops it). +3. **"Allow virtual network access"** is disabled on one or both peering links. +4. **"Allow forwarded traffic"** is disabled when traffic is being forwarded through an NVA. + +**Fix**: +```bash +# Check peering settings on both sides +az network vnet peering show -g RG-A --vnet-name VNetA -n AtoB \ + --query '{state:peeringState, allowVnetAccess:allowVirtualNetworkAccess, allowForwardedTraffic:allowForwardedTraffic}' + +# Check effective routes on the VM's NIC +az network nic show-effective-route-table -g MyRG -n MyVM-NIC + +# Check effective NSG rules +az network nic list-effective-nsg -g MyRG -n MyVM-NIC +``` + +### Asymmetric Peering Settings + +**Symptom**: Traffic works in one direction but not the other. +**Cause**: Peering options differ between the two sides (e.g., forwarded traffic allowed on one side but not the other). +**Fix**: Review and align peering options on both VNets. For hub-spoke, ensure "Allow forwarded traffic" is enabled on the spoke-to-hub peering. + +### Cannot Create Peering — Address Space Overlap + +**Symptom**: Error stating address spaces overlap. +**Fix**: Remove the overlapping address space from one VNet. If both spaces are in use, you may need to re-IP one of the VNets. + +```bash +# Check address spaces of both VNets +az network vnet show -g RG-A -n VNetA --query 'addressSpace.addressPrefixes' +az network vnet show -g RG-B -n VNetB --query 'addressSpace.addressPrefixes' +``` + +### Peering Disconnected After Address Change + +**Symptom**: Peering shows as "Disconnected" after modifying a VNet's address space. +**Cause**: Changing address space on a peered VNet can break the peering link. +**Fix**: Delete peering on both sides, make address space changes, then recreate peering. + +```bash +# Delete both peering links +az network vnet peering delete -g RG-A -n AtoB --vnet-name VNetA +az network vnet peering delete -g RG-B -n BtoA --vnet-name VNetB + +# Modify address space +az network vnet update -g RG-A -n VNetA --address-prefixes 10.0.0.0/16 10.3.0.0/16 + +# Recreate peering on both sides +az network vnet peering create -g RG-A -n AtoB --vnet-name VNetA --remote-vnet {VNetB-ID} --allow-vnet-access +az network vnet peering create -g RG-B -n BtoA --vnet-name VNetB --remote-vnet {VNetA-ID} --allow-vnet-access +``` diff --git a/plugin/skills/azure-virtual-network/references/service-endpoints.md b/plugin/skills/azure-virtual-network/references/service-endpoints.md new file mode 100644 index 000000000..85bfdbfed --- /dev/null +++ b/plugin/skills/azure-virtual-network/references/service-endpoints.md @@ -0,0 +1,238 @@ +# Service Endpoints Guide + +## Overview + +Virtual Network service endpoints extend your VNet's private address space and identity to Azure PaaS services over the Azure backbone network. When you enable a service endpoint on a subnet, traffic from that subnet to the target Azure service takes an optimized route over the Microsoft backbone — it never traverses the public internet. + +Service endpoints also allow you to lock down PaaS service access to specific VNets, adding a network layer of security on top of identity-based access control. + +## How Service Endpoints Work + +1. **Before service endpoints**: Traffic from a VM to Azure Storage goes through the VM's public IP (or NAT) over the internet, even though both resources are in Azure. +2. **After enabling service endpoints**: Traffic routes directly over the Azure backbone using the VM's private IP. The PaaS service sees the traffic originating from the VNet's private IP space. +3. **Firewall rules on the PaaS service**: You configure the PaaS service to only accept traffic from the VNet/subnet with the service endpoint — blocking all other public access. + +``` +Before: VM (10.0.1.4) → NAT → Internet → Storage (public endpoint) +After: VM (10.0.1.4) → Azure backbone → Storage (sees traffic from VNet) +``` + +## Supported Services + +| Service | Endpoint Name | Notes | +|---------|--------------|-------| +| Azure Storage | Microsoft.Storage | Blobs, Files, Queues, Tables | +| Azure SQL Database | Microsoft.Sql | Includes Azure Synapse Analytics | +| Azure Cosmos DB | Microsoft.AzureCosmosDB | All API types | +| Azure Key Vault | Microsoft.KeyVault | Secrets, keys, certificates | +| Azure Service Bus | Microsoft.ServiceBus | Messaging queues and topics | +| Azure Event Hubs | Microsoft.EventHub | Streaming ingestion | +| Azure App Service | Microsoft.Web | App Service and Functions | +| Azure Container Registry | Microsoft.ContainerRegistry | Container image pulls | +| Azure Cognitive Services | Microsoft.CognitiveServices | AI/ML APIs | +| Azure Data Lake Storage | Microsoft.Storage | Uses the Storage endpoint | + +> **Note**: New Azure services are increasingly offering private endpoints instead of (or in addition to) service endpoints. Check the latest documentation for each service. + +## Service Endpoints vs Private Endpoints + +| Feature | Service Endpoint | Private Endpoint | +|---------|-----------------|-----------------| +| Connectivity | Optimized route over backbone | Private IP in your VNet | +| PaaS service IP | Still uses public IP | Gets a private IP (10.x.x.x) | +| DNS | No DNS changes needed | Requires private DNS zone integration | +| On-premises access | Not accessible from on-prem | Accessible from on-prem via VPN/ExpressRoute | +| Cross-region | Same region only (by default) | Works across regions | +| Data exfiltration | Possible to other accounts of same service type | Locked to a specific resource instance | +| Cost | Free | Per hour + per GB processed | +| Setup complexity | Simple (subnet + PaaS firewall) | More complex (NIC, DNS, NSG) | + +### When to Use Which + +**Use service endpoints when**: +- Budget is a constraint (service endpoints are free). +- You only need VNet-to-PaaS access (no on-premises requirement). +- The service is in the same region as the VNet. +- You don't need per-resource-instance restriction (accepting any account of the service type is okay, or you use service endpoint policies). +- Simplicity is preferred over granularity. + +**Use private endpoints when** (preferred for new designs): +- You need on-premises access to the PaaS service via VPN/ExpressRoute. +- You want a private IP address for the PaaS service in your VNet. +- You need cross-region access. +- You need to restrict access to a specific resource instance (not just the service type). +- Compliance requires no public endpoint exposure. +- Data exfiltration prevention is critical. + +## Service Endpoint Policies + +Service endpoint policies let you restrict service endpoint access to specific Azure resource instances. Without policies, a service endpoint for Microsoft.Storage allows traffic to ALL Azure Storage accounts — including those in other subscriptions. Policies narrow this to specific accounts. + +```bash +# Create a service endpoint policy +az network service-endpoint policy create \ + -g MyRG -n StoragePolicy + +# Add a definition to allow only a specific storage account +az network service-endpoint policy-definition create \ + -g MyRG --policy-name StoragePolicy \ + -n AllowMyStorage \ + --service Microsoft.Storage \ + --service-resources "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/mystorageaccount" + +# Associate the policy with a subnet (along with the service endpoint) +az network vnet subnet update \ + -g MyRG --vnet-name MyVNet -n AppSubnet \ + --service-endpoints Microsoft.Storage \ + --service-endpoint-policy StoragePolicy +``` + +### Policy Limitations + +- Policies are currently supported only for **Azure Storage**. +- A subnet can have at most one service endpoint policy. +- Policies apply to all storage account access from the subnet — you cannot have different policies for different storage accounts from the same subnet. +- Policies are regional — they apply to storage accounts in the same region as the VNet. + +## Configuration Steps + +### Step 1: Enable the Service Endpoint on the Subnet + +```bash +# Enable service endpoint for Storage and SQL on a subnet +az network vnet subnet update \ + -g MyRG --vnet-name MyVNet -n AppSubnet \ + --service-endpoints Microsoft.Storage Microsoft.Sql +``` + +### Step 2: Configure PaaS Service Firewall Rules + +```bash +# Azure Storage: Add VNet rule +az storage account network-rule add \ + -g MyRG --account-name mystorageaccount \ + --vnet-name MyVNet --subnet AppSubnet + +# Azure Storage: Set default action to Deny (only allow configured networks) +az storage account update \ + -g MyRG -n mystorageaccount \ + --default-action Deny + +# Azure SQL: Add VNet rule +az sql server vnet-rule create \ + -g MyRG -s myserver -n AllowAppSubnet \ + --vnet-name MyVNet --subnet AppSubnet +``` + +### Step 3: Verify Connectivity + +```bash +# From a VM in AppSubnet, test connectivity to storage +# Should succeed because the service endpoint is active +az storage blob list --account-name mystorageaccount -c mycontainer --auth-mode login + +# From a VM NOT in AppSubnet (or from your local machine) +# Should be denied by the storage account firewall +``` + +## Regional Considerations + +- Service endpoints are configured at the **regional level** by default. +- When you enable `Microsoft.Storage` on a subnet, it enables the endpoint for the Storage service **in the same region** as the VNet. +- For **Azure Storage**, you can enable cross-region endpoints by also enabling `Microsoft.Storage.Global` — this extends the endpoint to all storage accounts in all regions. +- Other services (SQL, Key Vault, etc.) typically support cross-region service endpoints by default once the endpoint is enabled. + +```bash +# Enable global storage service endpoint (same + paired region) +az network vnet subnet update \ + -g MyRG --vnet-name MyVNet -n AppSubnet \ + --service-endpoints Microsoft.Storage Microsoft.Storage.Global +``` + +## Monitoring and Diagnostics + +### Verify Active Service Endpoints + +```bash +# Check which service endpoints are enabled on a subnet +az network vnet subnet show -g MyRG --vnet-name MyVNet -n AppSubnet \ + --query 'serviceEndpoints[].{service:service, status:provisioningState}' -o table +``` + +### Check PaaS Firewall Configuration + +```bash +# Check storage account network rules +az storage account show -g MyRG -n mystorageaccount \ + --query 'networkRuleSet.{defaultAction:defaultAction, vnets:virtualNetworkRules}' -o json + +# Check SQL server firewall rules +az sql server vnet-rule list -g MyRG -s myserver -o table +``` + +## Troubleshooting + +### Service Endpoint Not Working — Access Denied + +**Symptom**: Resources in the subnet get "access denied" when accessing the PaaS service. +**Causes**: +1. Service endpoint not yet provisioned (takes 15-60 seconds after enabling). +2. The PaaS service firewall does not have a VNet rule for the subnet. +3. The PaaS service default action is not set to Deny (the VNet rule exists but has no effect because "allow all" is still active). +4. Using an older API version that doesn't support service endpoints. + +**Fix**: Wait for provisioning. Verify the PaaS firewall has the correct VNet/subnet rule AND the default action is Deny. Recreate the endpoint if stuck. + +```bash +# Check endpoint provisioning state +az network vnet subnet show -g MyRG --vnet-name MyVNet -n AppSubnet \ + --query 'serviceEndpoints[].provisioningState' + +# Should return "Succeeded" for each endpoint +``` + +### Traffic Still Going Over Internet After Endpoint Enabled + +**Symptom**: Despite enabling the service endpoint, Network Watcher shows traffic going through a public IP. +**Causes**: +1. The application is using a public endpoint URL that resolves to a public IP — this is expected. Service endpoints change the routing path (backbone vs internet) but the DNS name still resolves to a public IP. +2. A UDR is overriding the service endpoint route (e.g., a 0.0.0.0/0 route to an NVA). + +**Fix**: Service endpoints are a routing optimization — the DNS resolution still shows a public IP, but the traffic actually traverses the backbone. Use Network Watcher's next-hop tool to verify the route. If a UDR is interfering, use effective routes to diagnose. + +### Cannot Remove Service Endpoint + +**Symptom**: Error when trying to remove a service endpoint from a subnet. +**Cause**: The PaaS service still has a VNet rule referencing this subnet. +**Fix**: Remove the VNet rule from the PaaS service first, then remove the service endpoint from the subnet. + +```bash +# Remove the VNet rule from storage +az storage account network-rule remove \ + -g MyRG --account-name mystorageaccount \ + --vnet-name MyVNet --subnet AppSubnet + +# Then remove the service endpoint +az network vnet subnet update \ + -g MyRG --vnet-name MyVNet -n AppSubnet \ + --service-endpoints '[]' +``` + +### Service Endpoint Policy Blocking Valid Traffic + +**Symptom**: Service endpoint policy blocks access to a storage account that should be allowed. +**Cause**: The storage account resource ID is not included in the policy definition. +**Fix**: Add the storage account to the policy definition. Remember that policy definitions use full resource IDs. + +```bash +# List current policy definitions +az network service-endpoint policy-definition list \ + -g MyRG --policy-name StoragePolicy -o table + +# Add the missing storage account +az network service-endpoint policy-definition create \ + -g MyRG --policy-name StoragePolicy \ + -n AllowAdditionalStorage \ + --service Microsoft.Storage \ + --service-resources "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/additionalstorage" +``` diff --git a/plugin/skills/azure-virtual-network/references/udr-guide.md b/plugin/skills/azure-virtual-network/references/udr-guide.md new file mode 100644 index 000000000..bd6250a6c --- /dev/null +++ b/plugin/skills/azure-virtual-network/references/udr-guide.md @@ -0,0 +1,326 @@ +# User-Defined Routes (UDR) Guide + +## Overview + +User-defined routes (UDRs) allow you to override Azure's default system routes, giving you fine-grained control over how network traffic is routed within and between subnets. UDRs are stored in route tables, which are then associated with one or more subnets. + +Every Azure subnet has a set of system routes that handle default traffic flows (intra-VNet, to the internet, to peered VNets, etc.). UDRs let you override these defaults — for example, to force all internet-bound traffic through a firewall instead of going directly to the internet. + +## When to Use UDRs + +- **Force tunneling**: Route all internet traffic through an on-premises firewall or VPN. +- **Hub-spoke routing**: Route spoke-to-spoke traffic through a hub firewall or NVA. +- **Network Virtual Appliance (NVA)**: Route traffic through a third-party firewall, IDS/IPS, or WAN optimizer. +- **Block internet access**: Route internet traffic to `None` to drop it. +- **Asymmetric routing fix**: Override BGP or system routes to ensure symmetric traffic flow. +- **Service-specific routing**: Direct traffic to specific Azure services through a particular path. + +## Next Hop Types + +Each UDR specifies a destination address prefix and a next hop. The next hop determines where the traffic goes. + +| Next Hop Type | Description | When to Use | +|---------------|-------------|-------------| +| **VirtualAppliance** | Traffic goes to a specific IP (NVA/firewall) | Route through Azure Firewall, third-party NVA | +| **VirtualNetworkGateway** | Traffic goes to the VPN/ExpressRoute gateway | Force traffic to on-premises via gateway | +| **VNetLocal** | Traffic stays within the VNet (override other routes) | Keep intra-VNet traffic local even when other routes exist | +| **Internet** | Traffic goes directly to the internet | Override a forced tunnel for specific prefixes | +| **None** | Traffic is dropped (black-holed) | Block traffic to a specific destination | + +### VirtualAppliance Example + +```bash +# Route all traffic through Azure Firewall (IP: 10.0.2.4) +az network route-table route create \ + -g MyRG --route-table-name SpokeRouteTable \ + -n DefaultToFirewall \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address 10.0.2.4 +``` + +### None Example (Block Traffic) + +```bash +# Block traffic to a specific range +az network route-table route create \ + -g MyRG --route-table-name RestrictedRouteTable \ + -n BlockExternal \ + --address-prefix 203.0.113.0/24 \ + --next-hop-type None +``` + +## Route Priority and Selection + +When multiple routes match a destination, Azure selects the route using this precedence: + +### Priority Order (Highest to Lowest) + +1. **Longest prefix match**: A /24 route beats a /16 route for the same destination. +2. **UDRs** (user-defined routes): Override system and BGP routes for the same prefix. +3. **BGP routes**: Routes learned from on-premises via VPN/ExpressRoute gateways. +4. **System routes**: Azure's built-in default routes. + +### System Route Defaults + +| Destination | Next Hop | Purpose | +|-------------|----------|---------| +| VNet address space | VNet local | Intra-VNet traffic | +| 0.0.0.0/0 | Internet | Default internet route | +| 10.0.0.0/8 | None | RFC 1918 drop (unless in VNet space) | +| 172.16.0.0/12 | None | RFC 1918 drop (unless in VNet space) | +| 192.168.0.0/16 | None | RFC 1918 drop (unless in VNet space) | +| Peered VNet prefix | VNet peering | Traffic to peered VNets | + +> **Key insight**: The 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 drop routes only apply to ranges NOT within the VNet's own address space. If your VNet uses 10.0.0.0/16, traffic to 10.0.x.x stays within the VNet — but traffic to 10.1.0.0/16 would be dropped unless you have a route for it. + +## Forced Tunneling + +Forced tunneling redirects all internet-bound traffic (0.0.0.0/0) to an on-premises location via VPN or ExpressRoute. This is common in regulated industries that require all traffic to pass through on-premises security appliances. + +### Via Azure Firewall (Recommended) + +```bash +# Create route table +az network route-table create -g MyRG -n ForcedTunnelRT + +# Add default route to Azure Firewall +az network route-table route create \ + -g MyRG --route-table-name ForcedTunnelRT \ + -n ForceTunnel \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address 10.0.2.4 + +# Associate with subnet +az network vnet subnet update \ + -g MyRG --vnet-name MyVNet -n AppSubnet \ + --route-table ForcedTunnelRT +``` + +### Via VPN Gateway (On-Premises) + +```bash +# Create route table with BGP propagation disabled +# (prevents VPN gateway from overriding the forced tunnel route) +az network route-table create -g MyRG -n OnPremTunnelRT \ + --disable-bgp-route-propagation true + +# Default route to VPN gateway +az network route-table route create \ + -g MyRG --route-table-name OnPremTunnelRT \ + -n ToOnPrem \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type VirtualNetworkGateway +``` + +> **Important**: When using forced tunneling, some Azure services (like Azure Backup, Windows activation) require direct internet access. Add specific UDRs with `--next-hop-type Internet` for the service IP ranges to keep them working. + +## Common Routing Scenarios + +### Scenario 1: Route All Spoke Traffic Through Hub Firewall + +``` +Spoke Subnet → UDR (0.0.0.0/0 → Firewall IP) → Hub Firewall → Internet or other spokes +``` + +```bash +# On the spoke subnet route table: +az network route-table route create \ + -g SpokeRG --route-table-name SpokeRT \ + -n ToFirewall \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address 10.0.2.4 + +# Also route to other spoke VNet ranges through the firewall: +az network route-table route create \ + -g SpokeRG --route-table-name SpokeRT \ + -n ToSpoke2 \ + --address-prefix 10.2.0.0/16 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address 10.0.2.4 +``` + +### Scenario 2: Block Internet Access from a Subnet + +```bash +az network route-table route create \ + -g MyRG --route-table-name NoInternetRT \ + -n BlockInternet \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type None +``` + +> **Warning**: This blocks ALL internet-bound traffic, including traffic to Azure PaaS services accessed via public endpoints. Ensure services use private endpoints or service endpoints before applying. + +### Scenario 3: Route to On-Premises via ExpressRoute + +When using ExpressRoute, BGP routes are automatically propagated. However, you might need UDRs to override specific BGP routes or force traffic through an NVA first. + +```bash +# If BGP learns a route to 172.16.0.0/12 via ExpressRoute, +# but you want that traffic to go through an NVA first: +az network route-table route create \ + -g MyRG --route-table-name CustomRT \ + -n OnPremViaNVA \ + --address-prefix 172.16.0.0/12 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address 10.0.3.4 +``` + +### Scenario 4: Keep Specific Traffic Local Despite Forced Tunnel + +```bash +# Force tunnel everything +az network route-table route create \ + -g MyRG --route-table-name MixedRT \ + -n ForceTunnel \ + --address-prefix 0.0.0.0/0 \ + --next-hop-type VirtualAppliance \ + --next-hop-ip-address 10.0.2.4 + +# But allow Azure Backup service tag IPs to go directly to internet +# (Requires knowing the Azure Backup IP ranges for your region) +az network route-table route create \ + -g MyRG --route-table-name MixedRT \ + -n AllowAzureBackup \ + --address-prefix 20.36.0.0/16 \ + --next-hop-type Internet +``` + +## Route Table Association + +Route tables are associated with subnets (not VNets or NICs). Every VM in the subnet follows the same route table. + +```bash +# Create and associate a route table +az network route-table create -g MyRG -n AppRouteTable +az network vnet subnet update \ + -g MyRG --vnet-name MyVNet -n AppSubnet \ + --route-table AppRouteTable + +# Remove route table association +az network vnet subnet update \ + -g MyRG --vnet-name MyVNet -n AppSubnet \ + --route-table "" +``` + +### Association Rules + +1. A subnet can have **at most one** route table associated. +2. A route table can be associated with **multiple subnets** (even across VNets in the same region). +3. If no route table is associated, the subnet uses system routes only. +4. Changing the route table takes effect within seconds — no VM restart needed. + +## Effective Routes + +The effective route table is the merged result of system routes, BGP routes, and UDRs for a specific NIC. This is what Azure actually uses for routing decisions. + +```bash +# View effective routes for a VM's NIC +az network nic show-effective-route-table -g MyRG -n MyVM-NIC -o table + +# Example output: +# Source State Address Prefix Next Hop Type Next Hop IP +# ------ ------ --------------- ----------------- ----------- +# Default Active 10.0.0.0/16 VnetLocal +# Default Active 0.0.0.0/0 Internet +# User Active 0.0.0.0/0 VirtualAppliance 10.0.2.4 +# Default Invalid 0.0.0.0/0 Internet (overridden by UDR) +``` + +**Reading effective routes**: +- **Source**: `Default` (system), `User` (UDR), `VirtualNetworkGateway` (BGP) +- **State**: `Active` (in use) or `Invalid` (overridden by a higher-priority route) +- When a UDR and system route have the same prefix, the UDR wins and the system route shows as "Invalid." + +## The 0.0.0.0/0 Route + +The `0.0.0.0/0` route is the default route — it matches ALL traffic that doesn't match a more specific route. It is the most commonly customized route. + +**Default behavior**: Azure routes 0.0.0.0/0 to the internet. + +**Common overrides**: +- 0.0.0.0/0 → VirtualAppliance (Azure Firewall or NVA) +- 0.0.0.0/0 → VirtualNetworkGateway (forced tunnel to on-premises) +- 0.0.0.0/0 → None (block all internet access) + +> **Warning**: Overriding 0.0.0.0/0 affects ALL outbound traffic not matched by a more specific route. This includes traffic to Azure management plane services. Ensure you add specific routes for Azure services that need direct connectivity. + +## BGP Route Propagation + +By default, routes learned from VPN/ExpressRoute gateways via BGP are propagated to all subnets in the VNet. You can disable this per route table. + +```bash +# Disable BGP propagation on a route table +az network route-table update -g MyRG -n MyRT --disable-bgp-route-propagation true +``` + +**When to disable BGP propagation**: +- On subnets where you want only UDRs to control routing (e.g., AzureFirewallSubnet). +- When BGP routes conflict with your UDR-based forced tunneling design. +- On management subnets that should not learn on-premises routes. + +## Troubleshooting + +### Asymmetric Routing + +**Symptom**: Traffic goes out one path but returns via a different path. Stateful firewalls drop the return traffic. +**Cause**: UDRs only control traffic in one direction. Return traffic may take a different route if the other subnet's route table does not send it back through the same path. +**Fix**: Ensure UDRs are configured on ALL subnets involved in the traffic flow to maintain symmetric routing. Both the source and destination subnets need route tables that point to the same firewall/NVA. + +### Black-Holed Traffic + +**Symptom**: Traffic disappears — no response and no errors. +**Cause**: A `None` next hop route is matching the traffic and dropping it, or a VirtualAppliance next hop points to a non-existent or down NVA. +**Fix**: Check effective routes. Verify the NVA IP is correct and the NVA is running and has IP forwarding enabled. + +```bash +# Check if IP forwarding is enabled on the NVA NIC +az network nic show -g MyRG -n NVA-NIC --query enableIpForwarding + +# Enable IP forwarding on the NVA NIC +az network nic update -g MyRG -n NVA-NIC --ip-forwarding true +``` + +### Missing Routes + +**Symptom**: Traffic to a peered VNet or on-premises is not flowing. +**Cause**: UDR overrides the system or BGP route for that destination. Or BGP propagation is disabled on the route table. +**Fix**: Check effective routes. Add a specific UDR for the missing destination, or re-enable BGP propagation if needed. + +### NVA Not Forwarding Traffic + +**Symptom**: UDR points to NVA but traffic doesn't reach the destination. +**Causes**: +1. **IP forwarding not enabled** on the NVA NIC (Azure setting). +2. **IP forwarding not enabled** inside the NVA OS (Linux: `net.ipv4.ip_forward=1`). +3. **NVA firewall rules** blocking the traffic. +4. **NVA has an NSG** that blocks the forwarded traffic. + +**Fix**: Enable IP forwarding at both the Azure NIC level and the OS level. Verify NVA firewall rules. + +```bash +# Enable at Azure level +az network nic update -g MyRG -n NVA-NIC --ip-forwarding true + +# Enable at Linux OS level (run inside the NVA VM) +# sudo sysctl -w net.ipv4.ip_forward=1 +# echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf +``` + +### Route Table Not Taking Effect + +**Symptom**: Route table is configured but traffic ignores it. +**Cause**: Route table is not associated with the correct subnet, or the specific route uses an incorrect prefix. +**Fix**: Verify association and check that the address prefix in the UDR matches the intended traffic. + +```bash +# Verify route table association +az network vnet subnet show -g MyRG --vnet-name MyVNet -n AppSubnet \ + --query 'routeTable.id' + +# List all routes in the table +az network route-table route list -g MyRG --route-table-name AppRouteTable -o table +``` diff --git a/plugin/skills/azure-virtual-network/references/vnet-fundamentals.md b/plugin/skills/azure-virtual-network/references/vnet-fundamentals.md new file mode 100644 index 000000000..83ccef4c8 --- /dev/null +++ b/plugin/skills/azure-virtual-network/references/vnet-fundamentals.md @@ -0,0 +1,236 @@ +# Azure Virtual Network Fundamentals + +## Overview + +An Azure Virtual Network (VNet) is the fundamental building block for private networking in Azure. It enables Azure resources such as VMs, App Services, and databases to securely communicate with each other, the internet, and on-premises networks. + +Each VNet is isolated from other VNets by default. Resources in one VNet cannot communicate with resources in another VNet unless you explicitly configure connectivity through peering, VPN gateways, or other mechanisms. + +VNets are scoped to a single Azure region and a single subscription. To span regions, you use global VNet peering or VPN/ExpressRoute connections. + +## Address Space Planning + +### RFC 1918 Private Address Ranges + +Azure VNets support the following private address ranges defined in RFC 1918: + +| Range | CIDR | Total Addresses | Common Use | +|-------|------|-----------------|------------| +| 10.0.0.0 – 10.255.255.255 | 10.0.0.0/8 | 16,777,216 | Large enterprise networks | +| 172.16.0.0 – 172.31.255.255 | 172.16.0.0/12 | 1,048,576 | Medium deployments | +| 192.168.0.0 – 192.168.255.255 | 192.168.0.0/16 | 65,536 | Small labs, dev/test | + +Azure also supports: +- **100.64.0.0/10** (CGNAT range) — usable in VNets but may conflict with some ISP traffic +- **Public IP ranges you own** (BYOIP) — requires validation through Azure Custom IP Prefix + +### Recommended Address Space Strategy + +1. **Plan for growth**: allocate a larger CIDR block than currently needed (e.g., /16 instead of /20). +2. **Use a consistent scheme**: assign address ranges systematically across subscriptions and regions. +3. **Avoid overlaps**: overlapping address spaces prevent VNet peering and VPN connectivity. +4. **Document allocations**: maintain an IP Address Management (IPAM) registry. +5. **Align with on-premises**: if hybrid, coordinate with on-premises network teams to avoid conflicts. + +### Example Enterprise Address Plan + +| Environment | Region | VNet CIDR | Purpose | +|-------------|--------|-----------|---------| +| Hub | East US | 10.0.0.0/16 | Shared services, firewall, DNS | +| Spoke-Prod | East US | 10.1.0.0/16 | Production workloads | +| Spoke-Dev | East US | 10.2.0.0/16 | Development workloads | +| Hub | West US | 10.10.0.0/16 | DR shared services | +| Spoke-Prod | West US | 10.11.0.0/16 | DR production workloads | +| On-premises DC | — | 172.16.0.0/12 | Corporate data center | + +## Subnet Design Patterns + +### Hub-Spoke Pattern + +The hub VNet contains shared services; spoke VNets contain workload-specific resources. + +**Hub VNet (10.0.0.0/16) subnets:** + +| Subnet | CIDR | Usable IPs | Purpose | +|--------|------|------------|---------| +| AzureFirewallSubnet | 10.0.0.0/26 | 59 | Azure Firewall (required name and min /26) | +| AzureBastionSubnet | 10.0.1.0/26 | 59 | Azure Bastion (required name and min /26) | +| GatewaySubnet | 10.0.2.0/27 | 27 | VPN/ExpressRoute gateway (required name) | +| SharedServicesSubnet | 10.0.3.0/24 | 251 | DNS, AD DS, monitoring | +| ManagementSubnet | 10.0.4.0/24 | 251 | Jump boxes, DevOps agents | + +**Spoke VNet (10.1.0.0/16) subnets:** + +| Subnet | CIDR | Usable IPs | Purpose | +|--------|------|------------|---------| +| WebSubnet | 10.1.0.0/24 | 251 | Front-end web servers | +| AppSubnet | 10.1.1.0/24 | 251 | Application tier | +| DataSubnet | 10.1.2.0/24 | 251 | Database tier | +| IntegrationSubnet | 10.1.3.0/24 | 251 | API Management, Logic Apps | + +### Tiered Application Pattern + +For a single VNet hosting a traditional 3-tier application: + +| Subnet | CIDR | NSG | Purpose | +|--------|------|-----|---------| +| FrontendSubnet | 10.0.0.0/24 | Allow 80/443 inbound from Internet | Load balancers, web servers | +| BackendSubnet | 10.0.1.0/24 | Allow app ports from FrontendSubnet only | Application servers | +| DatabaseSubnet | 10.0.2.0/24 | Allow DB ports from BackendSubnet only | SQL, Cosmos DB endpoints | +| ManagementSubnet | 10.0.3.0/24 | Allow SSH/RDP from Bastion only | Admin access, jump boxes | + +### Workload Isolation Pattern + +Separate VNets per workload with peering through a hub: + +- Each workload team owns their VNet and subnets. +- The hub VNet provides DNS, firewall, and gateway services. +- Peering connects spokes to the hub; spokes cannot communicate directly (non-transitive). +- Network Virtual Appliances (NVAs) or Azure Firewall enable spoke-to-spoke traffic when needed. + +## Azure Reserved Addresses + +Azure reserves 5 IP addresses in every subnet — the first 4 and the last 1: + +| Offset | Address Example (/24) | Purpose | +|--------|----------------------|---------| +| +0 | 10.0.0.0 | Network identifier | +| +1 | 10.0.0.1 | Default gateway for the subnet | +| +2 | 10.0.0.2 | Azure DNS (primary) | +| +3 | 10.0.0.3 | Azure DNS (secondary) | +| Last | 10.0.0.255 | Network broadcast | + +**Usable addresses per CIDR:** + +| CIDR | Total | Usable | % Usable | +|------|-------|--------|----------| +| /28 | 16 | 11 | 69% | +| /27 | 32 | 27 | 84% | +| /26 | 64 | 59 | 92% | +| /25 | 128 | 123 | 96% | +| /24 | 256 | 251 | 98% | +| /23 | 512 | 507 | 99% | +| /22 | 1,024 | 1,019 | 99.5% | +| /20 | 4,096 | 4,091 | 99.9% | +| /16 | 65,536 | 65,531 | ~100% | + +The smallest supported subnet in Azure is **/29** (8 addresses, 3 usable). However, some services require larger minimums — for example, AzureFirewallSubnet and AzureBastionSubnet require at minimum **/26**. + +## Subnet Delegation + +Subnet delegation assigns a subnet to a specific Azure service, giving that service permission to inject service-specific resources and configure networking. + +### Delegated Services (Common) + +| Service | Delegation Name | Min Subnet | +|---------|----------------|------------| +| Azure App Service | Microsoft.Web/serverFarms | /26 | +| Azure Container Instances | Microsoft.ContainerInstance/containerGroups | /24 recommended | +| Azure SQL Managed Instance | Microsoft.Sql/managedInstances | /27 (dedicated subnet) | +| Azure NetApp Files | Microsoft.Netapp/volumes | /28 | +| Azure Databricks | Microsoft.Databricks/workspaces | /26 per workspace | +| Azure API Management (v2) | Microsoft.ApiManagement/service | /27 | + +### Delegation Rules + +1. A subnet can be delegated to **only one service** at a time. +2. You **cannot** deploy non-delegated resources (like VMs) into a delegated subnet. +3. Removing delegation may require deleting all resources in the subnet first. +4. NSGs and UDRs still apply to delegated subnets (with some service-specific exceptions). +5. Plan delegated subnets separately — they cannot be shared with other workloads. + +## VNet Limits + +| Resource | Default Limit | Max (with support request) | +|----------|--------------|---------------------------| +| VNets per subscription per region | 1,000 | 1,000 | +| Subnets per VNet | 3,000 | 3,000 | +| VNet peerings per VNet | 500 | 500 (hard limit) | +| Address spaces per VNet | 100 | Varies | +| Private IP addresses per VNet | 65,536 | 65,536 | +| NSGs per subscription per region | 5,000 | 5,000 | +| Rules per NSG | 1,000 | 1,000 | +| Public IP addresses per subscription per region | 1,000 | Contact support | +| Route tables per subscription per region | 1,000 | 1,000 | +| Routes per route table | 400 | 400 | + +> **Note**: Limits are per subscription per region. Request increases through Azure Support if needed. Some limits (like peerings per VNet) are hard limits that cannot be increased. + +## VNet Encryption + +VNet encryption encrypts traffic between VMs within the same VNet and across peered VNets. It provides an additional layer of protection for data in transit within Azure's backbone. + +### Requirements + +- VMs must support **Accelerated Networking** (required for encryption). +- Supported VM SKUs include most Dv4/Ev4 and newer generation series. +- Both source and destination VMs must have Accelerated Networking enabled. +- Encryption is configured at the VNet level and applies to all VM-to-VM traffic. +- Available in specific regions — check Azure documentation for current availability. + +### Enabling VNet Encryption + +```bash +# Enable encryption on an existing VNet +az network vnet update -g MyRG -n MyVNet --enable-encryption true \ + --encryption-enforcement-policy AllowUnencrypted + +# Enforcement policies: +# - AllowUnencrypted: Allows both encrypted and unencrypted traffic (gradual rollout) +# - DropUnencrypted: Drops traffic from VMs that don't support encryption +``` + +### Considerations + +- **DropUnencrypted** enforcement will block traffic from VMs without Accelerated Networking — use `AllowUnencrypted` initially and switch after verifying all VMs are compatible. +- VNet encryption is separate from and complementary to application-level TLS/SSL. +- No performance degradation for supported VM SKUs — encryption is offloaded to hardware. +- Works across VNet peering when both VNets have encryption enabled. + +## Best Practices + +1. **Always plan address spaces before deploying** — changing VNet address space later can disrupt peerings and gateways. +2. **Use /16 for hub VNets** — leaves room for dozens of subnets with growth. +3. **Use /24 as the default subnet size** — 251 usable IPs covers most workloads with room to spare. +4. **Name subnets descriptively** — include the workload or tier name (e.g., `AppGw-Subnet`, `AKS-Nodes`). +5. **Assign NSGs to subnets, not NICs** — simplifies management and reduces rule duplication. +6. **Use tags consistently** — tag VNets and subnets with environment, owner, and cost center. +7. **Enable diagnostics logging** — send VNet flow logs to Log Analytics for monitoring and compliance. +8. **Automate with IaC** — use Bicep, Terraform, or ARM templates for repeatable VNet deployments. + +## Troubleshooting + +### Address Space Conflicts + +**Symptom**: Cannot create VNet peering — error about overlapping address spaces. +**Cause**: The two VNets share at least one overlapping CIDR block. +**Fix**: Resize one VNet's address space or use NAT (Azure VNet NAT or NVA) if resizing is not possible. + +### Subnet Too Small + +**Symptom**: Cannot deploy resources — "not enough available addresses in the subnet." +**Cause**: The subnet CIDR does not have enough free IPs for the requested resources. +**Fix**: Resize the subnet (requires removing all resources first) or create a new larger subnet. + +### Resources Cannot Communicate Within VNet + +**Symptom**: VMs in the same VNet cannot reach each other. +**Cause**: NSG rules blocking traffic, or VMs are in subnets with restrictive UDRs. +**Fix**: Check effective security rules (`az network nic list-effective-nsg`), check effective routes (`az network nic show-effective-route-table`), and verify no `None` next-hop UDR is blocking traffic. + +### Cannot Add Address Space to Existing VNet + +**Symptom**: Error when trying to add a new address space to a VNet. +**Cause**: New address space overlaps with existing address space or peered VNet address space. +**Fix**: Choose a non-overlapping range. If peered, you may need to delete and recreate peering after the change. + +### Subnet Deletion Fails + +**Symptom**: Cannot delete a subnet — "subnet is in use." +**Cause**: Resources (NICs, private endpoints, delegations, service endpoints) still exist in the subnet. +**Fix**: Remove all resources from the subnet first, then remove delegation if present, then delete. + +```bash +# Check what's in a subnet +az network vnet subnet show -g MyRG --vnet-name MyVNet -n MySubnet --query '{delegation:delegations, serviceEndpoints:serviceEndpoints, ipConfigurations:ipConfigurations}' +``` diff --git a/plugin/skills/azure-virtual-wan/SKILL.md b/plugin/skills/azure-virtual-wan/SKILL.md new file mode 100644 index 000000000..e5ef76f8d --- /dev/null +++ b/plugin/skills/azure-virtual-wan/SKILL.md @@ -0,0 +1,126 @@ +--- +name: azure-virtual-wan +description: "Deploy and manage Azure Virtual WAN for managed hub-and-spoke networking at scale, including vWAN hubs, secured virtual hubs with Azure Firewall, routing intent, SD-WAN integration, and NVA-in-hub deployments. WHEN: virtual wan, vwan, virtual WAN hub, hub and spoke, branch connectivity, SD-WAN, secured virtual hub, routing intent, any-to-any routing. DO NOT USE FOR: simple VNet peering (use azure-virtual-network), single-site VPN only (use azure-vpn-gateway), standalone firewall management (use azure-firewall)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Virtual WAN + +## When to Use This Skill + +- Deploying a managed hub-and-spoke network topology across multiple Azure regions +- Automating branch connectivity using SD-WAN partner integrations +- Enabling any-to-any transit connectivity between branches, VNets, and remote users +- Creating secured virtual hubs with Azure Firewall or third-party security-as-a-service (SECaaS) +- Configuring routing intent and routing policies for centralized traffic inspection +- Deploying network virtual appliances (NVAs) directly into the vWAN hub +- Integrating ExpressRoute circuits into the vWAN hub (see azure-expressroute) +- Setting up S2S VPN, P2S VPN, or ExpressRoute gateways within a vWAN hub +- Migrating from traditional hub-and-spoke with custom routing to vWAN-managed routing +- Connecting multiple on-premises sites across regions with transit routing through Azure backbone + +## Rules + +1. **Choose the right vWAN type.** Basic vWAN supports S2S VPN only. Standard vWAN supports S2S, P2S, ExpressRoute, inter-hub transit, VNet-to-VNet transit, and NVA-in-hub. Almost always choose Standard. +2. **One hub per region.** Each vWAN can have one hub per Azure region. Hubs automatically mesh for inter-hub transit. +3. **Hub address space cannot overlap.** Hub CIDR must not overlap with connected VNets or on-premises ranges. Minimum /24, recommended /23 for future NVA growth. +4. **Routing intent replaces custom route tables for secured hubs.** When you enable routing intent with Azure Firewall, all private and/or internet traffic routes through the firewall automatically. Do not try to mix routing intent with custom route tables on the same hub. +5. **Secured virtual hub = hub + firewall.** A secured virtual hub is a vWAN hub with Azure Firewall (or SECaaS) deployed inside it, managed through Azure Firewall Manager. +6. **VNet connections are not peerings.** Connecting a VNet to a vWAN hub is a managed connection, not traditional VNet peering. You cannot apply NSGs to the hub side. +7. **NVA-in-hub requires partner support.** Only validated partners (Barracuda, Cisco, Fortinet, VMware, Versa) can deploy NVAs inside the hub. You cannot deploy arbitrary NVAs. +8. **Gateway scale units determine throughput.** S2S VPN: 1 scale unit = 500 Mbps, up to 20 units (10 Gbps). ExpressRoute: 1 scale unit = 2 Gbps, up to 10 units (20 Gbps). +9. **ExpressRoute in vWAN vs standalone.** vWAN ExpressRoute gateways support up to 20 Gbps and auto-connect to the hub routing infrastructure. Standalone gateways require manual UDR configuration. +10. **Migration from hub-and-spoke.** You can migrate existing hub-and-spoke to vWAN but plan for downtime during VNet connection migration. Hub gateway VMs (VPN/ER) in the old hub must be removed first. + +## MCP Tools + +| Tool | Operation | Purpose | +|------|-----------|---------| +| `azure__network` | `virtual_wan_list` | List all Virtual WANs in a subscription or resource group | + +## CLI Fallback + +```bash +# List virtual WANs +az network vwan list --resource-group + +# Create a virtual WAN +az network vwan create \ + --name \ + --resource-group \ + --type Standard + +# Create a virtual hub +az network vhub create \ + --name \ + --resource-group \ + --vwan \ + --address-prefix 10.0.0.0/23 \ + --location + +# Show virtual hub details +az network vhub show --name --resource-group + +# Connect a VNet to the hub +az network vhub connection create \ + --name \ + --resource-group \ + --vhub-name \ + --remote-vnet + +# Create S2S VPN gateway in hub +az network vpn-gateway create \ + --name \ + --resource-group \ + --vhub \ + --scale-unit 1 + +# Create ExpressRoute gateway in hub +az network express-route gateway create \ + --name \ + --resource-group \ + --virtual-hub \ + --min-val 2 + +# List hub route tables +az network vhub route-table list \ + --resource-group \ + --vhub-name + +# Show effective routes for a connection +az network vhub get-effective-routes \ + --resource-group \ + --name \ + --resource-type VirtualHubVnetConnection \ + --resource-id + +# List VNet connections on a hub +az network vhub connection list \ + --resource-group \ + --vhub-name +``` + +## Key Concepts + +- **vWAN types:** Basic (S2S VPN only) and Standard (full feature set: S2S, P2S, ER, VNet transit, inter-hub, NVA-in-hub). +- **Virtual hub:** Microsoft-managed VNet in each region acting as the connectivity nexus. Contains gateway VMs, route tables, and optional NVAs or firewalls. +- **Hub routing:** Automatic route propagation across all connections (VNet, VPN, ER). Default route table receives all routes and propagates to all connections. +- **Routing intent:** A policy that directs internet traffic, private traffic, or both through a next-hop security solution (Azure Firewall or NVA) in the hub. Simplifies secured hub routing. +- **Secured virtual hub:** A vWAN hub with Azure Firewall or SECaaS deployed, managed via Firewall Manager. Supports DNAT, network rules, application rules, threat intelligence, and IDPS. +- **Inter-hub transit:** Traffic between hubs in different regions flows over the Microsoft backbone (global vWAN transit). No user configuration needed beyond hub creation. +- **NVA-in-hub:** Deploy supported third-party NVAs (firewalls, SD-WAN controllers) directly inside the hub for in-line traffic inspection or SD-WAN optimization. +- **Scale units:** VPN gateway scale units (500 Mbps each), ExpressRoute gateway scale units (2 Gbps each), P2S gateway scale units (500 Mbps each). +- **Connection types:** VNet connections (spoke VNets), VPN site connections (branches), ExpressRoute connections (circuits), User VPN connections (P2S). +- **SD-WAN integration:** Validated partners can automate branch-to-hub connectivity through the vWAN REST API. Partners include Cisco Viptela, VMware SD-WAN, Versa, and others. + +## References + +- [references/vwan-architecture.md](references/vwan-architecture.md) — vWAN types, hub components, transit connectivity +- [references/routing-intent.md](references/routing-intent.md) — Routing intent and routing policies +- [references/secured-hub.md](references/secured-hub.md) — Secured virtual hub with Azure Firewall +- [references/nva-in-hub.md](references/nva-in-hub.md) — NVA-in-hub deployment and routing +- [Azure Virtual WAN documentation](https://learn.microsoft.com/azure/virtual-wan/) +- [Virtual WAN FAQ](https://learn.microsoft.com/azure/virtual-wan/virtual-wan-faq) diff --git a/plugin/skills/azure-virtual-wan/references/nva-in-hub.md b/plugin/skills/azure-virtual-wan/references/nva-in-hub.md new file mode 100644 index 000000000..afb64f2e6 --- /dev/null +++ b/plugin/skills/azure-virtual-wan/references/nva-in-hub.md @@ -0,0 +1,225 @@ +# NVA-in-Hub Deployment + +## Overview + +Network Virtual Appliances (NVAs) in the vWAN hub allow you to deploy supported third-party networking solutions directly inside the virtual hub. This enables SD-WAN optimization, custom firewalling, and traffic inspection without deploying NVAs in spoke VNets with complex UDR management. + +## Supported Partners + +NVA-in-hub requires validation from both Microsoft and the NVA vendor. Only the following partners are supported: + +| Partner | NVA Type | Primary Use Case | +|---------|----------|-----------------| +| **Barracuda Networks** | CloudGen WAN | SD-WAN, firewall | +| **Cisco** | Catalyst SD-WAN (Viptela) | SD-WAN | +| **Fortinet** | FortiGate Next-Gen Firewall | Firewall, SD-WAN | +| **VMware** | SD-WAN (VeloCloud) | SD-WAN | +| **Versa Networks** | SD-WAN | SD-WAN | +| **Check Point** | CloudGuard Network Security | Firewall | +| **Palo Alto Networks** | Cloud NGFW | Firewall (SaaS model) | + +**You cannot deploy arbitrary NVA images** (custom VMs, marketplace VMs) directly in the hub. Only validated partner solutions are supported through the managed application framework. + +## Architecture + +``` + ┌────────────────────────────────┐ + │ Virtual Hub │ + │ │ + Branch ─── VPN GW ─┤ ├── Spoke VNet A + │ ┌──────────┐ │ + Branch ─── SD-WAN ─┤──────►│ NVA │─────────────├── Spoke VNet B + │ │ (Partner)│ │ + On-Prem ── ER GW ──┤──────►│ │─────────────├── Spoke VNet C + │ └──────────┘ │ + └────────────────────────────────┘ +``` + +NVAs sit in the data path within the hub, intercepting traffic based on routing configuration. + +## Deployment Process + +### Step 1: Ensure Standard vWAN and Hub Exist + +```bash +# Verify vWAN type is Standard +az network vwan show --name --resource-group --query type + +# Verify hub exists +az network vhub show --name --resource-group +``` + +### Step 2: Deploy NVA via Azure Marketplace + +NVA-in-hub deployment is typically done through: +1. **Azure Marketplace** — search for the partner's vWAN NVA offering +2. **Partner portal** — some partners provide direct deployment from their management console +3. **ARM/Bicep templates** — for infrastructure-as-code deployments + +The deployment creates a **managed application** in your subscription, which provisions the NVA instances inside the hub. + +### Step 3: Configure NVA Scale Units + +NVA-in-hub supports scaling through infrastructure units (similar to gateway scale units): + +| Infrastructure Units | Approximate Throughput | +|---------------------|----------------------| +| 2 | 500 Mbps - 1 Gbps | +| 4 | 1 - 2 Gbps | +| 10 | 2 - 5 Gbps | +| 20 | 5 - 10 Gbps | + +Exact throughput depends on the NVA partner and configuration. Refer to partner documentation for sizing. + +### Step 4: Configure NVA Through Partner Management + +After deployment, NVA configuration (firewall rules, SD-WAN policies, routing) is managed through the **partner's management interface**, not through Azure: + +- **Barracuda:** CloudGen WAN portal +- **Cisco:** vManage +- **Fortinet:** FortiManager / FortiGate management +- **VMware:** VMware SD-WAN Orchestrator +- **Versa:** Versa Director + +## Routing with NVA-in-Hub + +### Advertising Routes from NVA + +NVAs in the hub can advertise routes via BGP to the hub's routing infrastructure: + +``` +NVA → BGP → Hub Route Table → All Connections (VNets, VPN, ER) +``` + +The NVA establishes a BGP session with the hub's route service using: +- Hub's BGP peer IPs (provided during NVA deployment) +- NVA's own BGP IP (configured in the NVA) + +### Directing Traffic Through NVA + +To force traffic through the NVA, you have two options: + +**Option A: Routing Intent (Recommended)** +If the NVA supports being the next-hop for routing intent, configure routing intent with the NVA as the next hop: + +```bash +az network vhub routing-intent create \ + --name \ + --resource-group \ + --vhub \ + --routing-policies "[{name:PrivateTraffic,destinations:[PrivateTraffic],nextHop:}]" +``` + +**Option B: Static Routes in Hub Route Table** +For more granular control, add static routes pointing to the NVA: + +```bash +az network vhub route-table route add \ + --resource-group \ + --vhub-name \ + --route-table-name defaultRouteTable \ + --destinations 10.0.0.0/8 \ + --destination-type CIDR \ + --next-hop \ + --next-hop-type ResourceId +``` + +### BGP Peering with NVA + +The NVA peers with the hub's route service over BGP: + +``` +NVA BGP ASN: (configured per partner, e.g., 65222) +Hub BGP ASN: 65515 +BGP Peer IPs: provided by the hub during NVA provisioning +``` + +Routes advertised by the NVA are propagated to all hub connections (VNets, branches) based on route table associations. + +## NVA-in-Hub vs NVA-in-Spoke + +| Consideration | NVA-in-Hub | NVA-in-Spoke | +|-------------|-----------|-------------| +| UDR management | Minimal (hub routing handles it) | Complex (UDRs on every spoke subnet) | +| Scaling | Managed scale units | Manual VM scaling | +| Vendor support | Only validated partners | Any NVA from marketplace | +| Management | Partner management console | Direct VM access | +| Availability | Hub-managed redundancy | Self-managed (VMSS, LB) | +| Cost | Partner pricing + hub costs | VM pricing + LB + management | +| Flexibility | Limited to partner capabilities | Full VM customization | + +**Choose NVA-in-hub** when: +- Using a validated SD-WAN partner for branch connectivity +- Wanting simplified routing without complex UDR management +- Partner provides the specific NVA features needed + +**Choose NVA-in-spoke** when: +- Using a vendor not validated for NVA-in-hub +- Needing full control over NVA configuration and networking +- Custom NVA or marketplace appliance required + +## Coexistence: NVA + Azure Firewall + +You can deploy both an NVA and Azure Firewall in the same hub: + +- **NVA** handles SD-WAN optimization and branch connectivity +- **Azure Firewall** handles security inspection (with routing intent) + +Traffic flow: +``` +Branch → NVA (SD-WAN) → Azure Firewall (security) → Spoke VNet +``` + +Configure routing intent with Azure Firewall as the security next-hop, while the NVA handles the branch connectivity overlay. + +## Monitoring NVA-in-Hub + +### Azure Monitor Metrics + +NVAs expose metrics through Azure Monitor: +- Throughput (ingress/egress) +- CPU utilization +- Memory utilization +- Connection count + +### Partner Monitoring + +Detailed NVA monitoring (rule hits, SD-WAN performance, session logs) is managed through the partner's monitoring tools, not Azure Monitor. + +### Health Probes + +The hub monitors NVA health automatically. If an NVA instance becomes unhealthy: +- Traffic is redirected to healthy instances +- The platform attempts to recover the unhealthy instance +- Alerts can be configured in Azure Monitor + +## Troubleshooting + +### NVA Deployment Fails + +1. **Check vWAN type** — must be Standard +2. **Check hub address space** — ensure /23 or larger for NVA deployment room +3. **Check subscription quota** — NVA deployment creates managed resources that consume quota +4. **Check partner prerequisites** — some partners require pre-registration or licensing + +### Traffic Not Flowing Through NVA + +1. **Check routing** — verify hub route table shows NVA as next hop for target prefixes +2. **Check BGP peering** — NVA must have active BGP sessions with the hub +3. **Check NVA health** — verify NVA instances are healthy in the managed application +4. **Check NVA rules** — verify the NVA's firewall/routing rules allow the traffic +5. **Check routing intent** — if using routing intent, verify NVA is the configured next hop + +### Performance Issues + +1. **Scale up** — increase infrastructure units for more throughput +2. **Check NVA CPU/memory** — partner monitoring tools show NVA resource utilization +3. **Check MTU** — encapsulation overhead may cause fragmentation; verify MTU settings +4. **Check partner logs** — SD-WAN optimization or deep inspection may add latency + +## Additional References + +- [NVA in virtual hub](https://learn.microsoft.com/azure/virtual-wan/about-nva-hub) +- [Deploy NVA in vWAN hub](https://learn.microsoft.com/azure/virtual-wan/how-to-nva-hub) +- [BGP peering with NVA](https://learn.microsoft.com/azure/virtual-wan/scenario-bgp-peering-hub) +- [Supported NVA partners](https://learn.microsoft.com/azure/virtual-wan/about-nva-hub#partners) diff --git a/plugin/skills/azure-virtual-wan/references/routing-intent.md b/plugin/skills/azure-virtual-wan/references/routing-intent.md new file mode 100644 index 000000000..cad6ae929 --- /dev/null +++ b/plugin/skills/azure-virtual-wan/references/routing-intent.md @@ -0,0 +1,195 @@ +# Routing Intent and Routing Policies + +## Overview + +Routing intent is a feature that simplifies traffic routing through a next-hop security solution (Azure Firewall or supported NVA) in a Virtual WAN hub. Instead of manually configuring route tables and static routes, routing intent automatically programs the hub to send internet traffic, private traffic, or both through the security solution. + +## Key Concepts + +### Routing Policies + +Routing intent supports two routing policies that can be enabled independently or together: + +| Policy | Traffic Affected | Next Hop | +|--------|-----------------|----------| +| **Internet Traffic** | Traffic destined for the internet (0.0.0.0/0) | Azure Firewall or NVA in the hub | +| **Private Traffic** | Traffic between VNets, branches, and other private ranges (RFC 1918: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) | Azure Firewall or NVA in the hub | + +### Traffic Flows with Routing Intent + +When **both** policies are enabled: + +| Source → Destination | Path | +|---------------------|------| +| VNet → Internet | VNet → Hub Firewall → Internet | +| VNet → VNet (same hub) | VNet A → Hub Firewall → VNet B | +| VNet → VNet (different hub) | VNet A → Hub 1 Firewall → Hub 2 Firewall → VNet B | +| VNet → Branch (VPN/ER) | VNet → Hub Firewall → Branch | +| Branch → VNet | Branch → Hub Firewall → VNet | +| Branch → Internet | Branch → Hub Firewall → Internet | +| Branch → Branch | Branch A → Hub Firewall → Branch B | + +All traffic inspected. No exceptions within the scope of enabled policies. + +When only **Internet Traffic** policy is enabled: +- Only 0.0.0.0/0 traffic goes through the firewall +- Private traffic (VNet-to-VNet, VNet-to-branch) routes directly without firewall inspection + +When only **Private Traffic** policy is enabled: +- Private traffic goes through the firewall +- Internet traffic follows default routing (breakout at the branch or VNet level) + +## Prerequisites + +- **Standard vWAN** (Basic does not support routing intent) +- **Azure Firewall** or **supported NVA** deployed in the hub (secured virtual hub) +- Routing intent **replaces** custom route tables when enabled — you cannot use both simultaneously on the same hub +- All spoke VNets connected to the hub must be associated with the default route table + +## Configuration + +### Enable Routing Intent with Azure Firewall + +First, deploy Azure Firewall in the hub (creating a secured virtual hub — see [references/secured-hub.md](secured-hub.md)). + +Then enable routing intent via Azure portal or CLI: + +```bash +# Enable both internet and private traffic routing through Azure Firewall +az network vhub routing-intent create \ + --name \ + --resource-group \ + --vhub \ + --routing-policies "[{name:InternetTraffic,destinations:[Internet],nextHop:},{name:PrivateTraffic,destinations:[PrivateTraffic],nextHop:}]" +``` + +### Enable Only Internet Traffic Policy + +```bash +az network vhub routing-intent create \ + --name \ + --resource-group \ + --vhub \ + --routing-policies "[{name:InternetTraffic,destinations:[Internet],nextHop:}]" +``` + +### Enable Only Private Traffic Policy + +```bash +az network vhub routing-intent create \ + --name \ + --resource-group \ + --vhub \ + --routing-policies "[{name:PrivateTraffic,destinations:[PrivateTraffic],nextHop:}]" +``` + +### View Routing Intent Configuration + +```bash +az network vhub routing-intent show \ + --name \ + --resource-group \ + --vhub +``` + +### Update Routing Policies + +```bash +# Add private traffic policy to existing internet-only intent +az network vhub routing-intent update \ + --name \ + --resource-group \ + --vhub \ + --routing-policies "[{name:InternetTraffic,destinations:[Internet],nextHop:},{name:PrivateTraffic,destinations:[PrivateTraffic],nextHop:}]" +``` + +### Remove Routing Intent + +```bash +az network vhub routing-intent delete \ + --name \ + --resource-group \ + --vhub +``` + +**Warning:** Removing routing intent removes automatic routing through the firewall. Traffic reverts to default vWAN routing (direct connectivity). + +## Inter-Hub Behavior + +When routing intent is enabled on **multiple hubs**: + +- Inter-hub private traffic is inspected by **both hub firewalls** (firewall in source hub and firewall in destination hub) +- This provides double inspection but may increase latency +- Each firewall applies its own policy set + +When routing intent is enabled on **one hub only**: + +- Traffic from the secured hub's spokes is inspected by that hub's firewall +- Traffic from the non-secured hub's spokes is not inspected (no firewall in that hub) +- For consistent security, enable routing intent on all hubs + +## Interaction with Custom Route Tables + +**Routing intent and custom route tables are mutually exclusive on the same hub.** + +When routing intent is enabled: +- All existing custom route table associations are overridden +- All connections are automatically associated with the default route table +- Static routes in custom route tables are not honored +- You manage security policy through the firewall (not route tables) + +If you need custom route table behavior (e.g., VNet isolation), you must disable routing intent and configure routing manually. + +## Interaction with VNet Connection Settings + +### Internet Security Flag + +For internet traffic policy, each VNet connection has an "Internet Security" flag: + +```bash +# Enable internet security for a VNet connection (routes internet traffic through firewall) +az network vhub connection update \ + --name \ + --resource-group \ + --vhub-name \ + --internet-security true +``` + +When routing intent with internet policy is enabled, this flag is automatically set to `true` for all connections. + +### Propagating Default Route + +When internet traffic policy is enabled, the hub automatically advertises a default route (0.0.0.0/0) to: +- All connected VNets (via VNet connection) +- All VPN sites (via BGP) +- All ExpressRoute connected sites (via BGP, if configured) + +This forces all internet-bound traffic through the hub firewall. + +## Troubleshooting + +### Traffic Not Flowing Through Firewall + +1. **Verify routing intent is enabled** — check `az network vhub routing-intent show` +2. **Check firewall status** — Azure Firewall must be running and healthy +3. **Check firewall rules** — traffic may be blocked by the firewall (not a routing issue) +4. **Verify effective routes** — `az network vhub get-effective-routes` should show the firewall as next hop +5. **VNet connection association** — all connections must be in the default route table + +### Internet Access Broken After Enabling Internet Policy + +1. **Firewall rules needed** — you must create firewall rules to allow internet traffic (DNAT, network, application rules) +2. **DNS configuration** — VNets may need Azure Firewall's private IP as their DNS server for FQDN-based rules +3. **Asymmetric routing** — ensure return traffic from the internet flows back through the firewall + +### Private Connectivity Broken After Enabling Private Policy + +1. **Firewall rules** — create network rules to allow VNet-to-VNet and VNet-to-branch traffic +2. **Check RFC 1918 coverage** — routing intent covers 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 +3. **Non-RFC 1918 private ranges** — if using other private ranges, they may not be covered by the private traffic policy + +## Additional References + +- [Virtual WAN routing intent](https://learn.microsoft.com/azure/virtual-wan/how-to-routing-policies) +- [Routing intent concepts](https://learn.microsoft.com/azure/virtual-wan/routing-intent-concepts) +- [Routing intent FAQ](https://learn.microsoft.com/azure/virtual-wan/routing-intent-faq) diff --git a/plugin/skills/azure-virtual-wan/references/secured-hub.md b/plugin/skills/azure-virtual-wan/references/secured-hub.md new file mode 100644 index 000000000..55ded449a --- /dev/null +++ b/plugin/skills/azure-virtual-wan/references/secured-hub.md @@ -0,0 +1,243 @@ +# Secured Virtual Hub + +## Overview + +A secured virtual hub is an Azure Virtual WAN hub with Azure Firewall (or a supported third-party security-as-a-service provider) deployed inside it. The hub and firewall are managed together through Azure Firewall Manager, providing centralized security policy management across multiple hubs and regions. + +## Architecture + +``` + ┌─────────────────────────────────┐ + │ Secured Virtual Hub │ + │ │ + Branch ─── VPN GW ─── Azure Firewall ─── VNet Connections ─── Spoke VNets + │ │ │ + On-Prem ── ER GW ────────┘ │ + │ │ + Users ─── P2S GW ─────────────────────────────────│ + └─────────────────────────────────┘ +``` + +All traffic between connections (VPN, ER, VNet, P2S) can be inspected by the Azure Firewall when routing intent is enabled. + +## Deploying a Secured Virtual Hub + +### Option 1: Deploy Azure Firewall in an Existing Hub + +```bash +# Deploy Azure Firewall in the hub +# This is typically done through Firewall Manager in the Azure portal +# CLI alternative: create a firewall policy first, then associate with the hub + +# Create a firewall policy +az network firewall policy create \ + --name \ + --resource-group \ + --sku Premium \ + --threat-intel-mode Deny \ + --idps-mode Deny + +# Deploy Azure Firewall in the vWAN hub (portal recommended) +# The hub becomes a "secured virtual hub" after firewall deployment +``` + +### Option 2: Create Hub as Secured from the Start + +Using Azure Firewall Manager in the portal: +1. Open Firewall Manager → Secured Virtual Hubs +2. Select "Create new secured virtual hub" +3. Specify vWAN, hub region, address space +4. Choose Azure Firewall tier (Standard or Premium) +5. Attach a firewall policy + +## Azure Firewall SKUs in vWAN + +| Feature | Standard | Premium | +|---------|----------|---------| +| Network rules (L3-L4) | Yes | Yes | +| Application rules (L7 FQDN) | Yes | Yes | +| Threat intelligence | Yes | Yes | +| DNAT rules | Yes | Yes | +| TLS inspection | No | Yes | +| IDPS (Intrusion Detection/Prevention) | No | Yes | +| URL filtering (full URL path) | No | Yes | +| Web categories | Limited | Full | + +**Recommendation:** Use Premium for production workloads requiring TLS inspection or IDPS. Use Standard for basic traffic filtering. + +## Firewall Policy Structure + +Firewall policies define the security rules applied to the secured hub. Policies support inheritance for multi-hub management. + +### Policy Hierarchy + +``` +Base Policy (global defaults) +├── Regional Policy A (inherits base + regional rules) +│ └── Hub Policy A1 (inherits regional + hub-specific rules) +└── Regional Policy B (inherits base + regional rules) + └── Hub Policy B1 (inherits regional + hub-specific rules) +``` + +### Create and Configure Firewall Policy + +```bash +# Create base firewall policy +az network firewall policy create \ + --name base-policy \ + --resource-group \ + --sku Premium \ + --threat-intel-mode Alert + +# Create child policy inheriting from base +az network firewall policy create \ + --name regional-policy \ + --resource-group \ + --sku Premium \ + --base-policy + +# Create a rule collection group +az network firewall policy rule-collection-group create \ + --name DefaultNetworkRuleGroup \ + --policy-name regional-policy \ + --resource-group \ + --priority 200 + +# Add network rule collection +az network firewall policy rule-collection-group collection add-filter-collection \ + --name AllowInternalTraffic \ + --policy-name regional-policy \ + --resource-group \ + --rule-collection-group-name DefaultNetworkRuleGroup \ + --collection-priority 100 \ + --action Allow \ + --rule-name AllowVNetToVNet \ + --rule-type NetworkRule \ + --source-addresses "10.0.0.0/8" \ + --destination-addresses "10.0.0.0/8" \ + --destination-ports "*" \ + --ip-protocols Any + +# Add application rule collection for internet access +az network firewall policy rule-collection-group collection add-filter-collection \ + --name AllowInternet \ + --policy-name regional-policy \ + --resource-group \ + --rule-collection-group-name DefaultNetworkRuleGroup \ + --collection-priority 200 \ + --action Allow \ + --rule-name AllowWeb \ + --rule-type ApplicationRule \ + --source-addresses "10.0.0.0/8" \ + --protocols Https=443 Http=80 \ + --target-fqdns "*.microsoft.com" "*.azure.com" +``` + +## Firewall Manager Integration + +Azure Firewall Manager provides a centralized management plane for secured virtual hubs: + +### Capabilities + +- **Single pane of glass** for managing firewalls across multiple hubs and regions +- **Security policy management** — create, assign, and update firewall policies +- **Security partner providers** — integrate third-party SECaaS (ZScaler, iBoss, Check Point) for internet traffic filtering +- **Routing intent management** — configure which traffic flows through the firewall +- **DDoS protection plan association** — link DDoS protection to the secured hub + +### Third-Party SECaaS Integration + +For internet traffic, you can use a third-party security partner instead of (or alongside) Azure Firewall: + +| Provider | Traffic Type | Integration | +|----------|-------------|-------------| +| ZScaler | Internet | Cloud proxy redirect | +| iBoss | Internet | Cloud proxy redirect | +| Check Point Harmony Connect | Internet | Cloud proxy redirect | + +SECaaS providers handle internet traffic filtering. Azure Firewall (or NVA) handles private traffic filtering. They can work together: +- **Internet traffic** → SECaaS provider +- **Private traffic** → Azure Firewall + +## DNAT Rules in Secured Hub + +DNAT (Destination NAT) rules allow inbound internet access to services behind the firewall. + +```bash +# Add DNAT rule collection +az network firewall policy rule-collection-group collection add-nat-collection \ + --name InboundDNAT \ + --policy-name \ + --resource-group \ + --rule-collection-group-name DefaultDnatRuleGroup \ + --collection-priority 100 \ + --action DNAT \ + --rule-name WebServer \ + --rule-type NatRule \ + --source-addresses "*" \ + --destination-addresses \ + --destination-ports 443 \ + --ip-protocols TCP \ + --translated-address 10.1.1.10 \ + --translated-port 443 +``` + +**Note:** In vWAN, Azure Firewall's public IP is managed by the platform. Retrieve it from the hub's firewall resource. + +## Monitoring and Logging + +### Diagnostic Settings + +```bash +# Enable firewall diagnostic logging +az monitor diagnostic-settings create \ + --name fw-diagnostics \ + --resource \ + --workspace \ + --logs '[{"category":"AzureFirewallNetworkRule","enabled":true},{"category":"AzureFirewallApplicationRule","enabled":true},{"category":"AzureFirewallDnsProxy","enabled":true}]' +``` + +### Key Log Categories + +| Category | What It Captures | +|----------|-----------------| +| AzureFirewallNetworkRule | Network rule matches (allow/deny) | +| AzureFirewallApplicationRule | Application rule matches (FQDN, URL) | +| AzureFirewallDnsProxy | DNS proxy queries | +| AzureFirewallThreatIntel | Threat intelligence matches | +| AzureFirewallIdpsSignature | IDPS signature matches (Premium) | + +### Workbooks and Dashboards + +Azure Firewall provides built-in workbooks in Azure Monitor: +- **Azure Firewall Workbook** — overview of rule hits, threat intel, blocked traffic +- **IDPS Workbook** (Premium) — intrusion detection/prevention alerts + +## Troubleshooting + +### Firewall Blocking Expected Traffic + +1. **Check rule order** — rules are processed in priority order within rule collection groups. Lower priority number = higher precedence. +2. **Check rule collection group order** — DNAT rules → Network rules → Application rules +3. **Check rule scope** — verify source/destination addresses cover the actual traffic IPs +4. **Check firewall logs** — query AzureFirewallNetworkRule and AzureFirewallApplicationRule logs in Log Analytics + +### DNS Issues with Secured Hub + +1. **Enable DNS proxy** — Azure Firewall should act as DNS proxy for spoke VNets +2. **Point VNet DNS** to the Azure Firewall private IP +3. **Configure firewall DNS settings** to use Azure DNS (168.63.129.16) or custom DNS servers + +### Performance Considerations + +- Azure Firewall Standard supports up to **30 Gbps** throughput +- Azure Firewall Premium supports up to **100 Gbps** throughput +- IDPS and TLS inspection add processing overhead — size firewall appropriately +- Use network rules (L3/L4) instead of application rules (L7) when FQDN filtering is not needed, for lower latency + +## Additional References + +- [Secured virtual hub overview](https://learn.microsoft.com/azure/firewall-manager/secured-virtual-hub) +- [Azure Firewall Manager](https://learn.microsoft.com/azure/firewall-manager/overview) +- [Deploy Azure Firewall in vWAN](https://learn.microsoft.com/azure/virtual-wan/howto-firewall) +- [Firewall policy rule processing](https://learn.microsoft.com/azure/firewall/rule-processing) diff --git a/plugin/skills/azure-virtual-wan/references/vwan-architecture.md b/plugin/skills/azure-virtual-wan/references/vwan-architecture.md new file mode 100644 index 000000000..137a4b036 --- /dev/null +++ b/plugin/skills/azure-virtual-wan/references/vwan-architecture.md @@ -0,0 +1,245 @@ +# Virtual WAN Architecture + +## Overview + +Azure Virtual WAN (vWAN) is a managed networking service that provides hub-and-spoke connectivity at scale. It replaces manually built hub VNets with Microsoft-managed virtual hubs that automate routing, connectivity, and security across branches, VNets, and remote users. + +## vWAN Types + +| Type | S2S VPN | P2S VPN | ExpressRoute | VNet-to-VNet Transit | Inter-Hub Transit | NVA-in-Hub | Routing Intent | +|------|---------|---------|-------------|---------------------|-------------------|------------|---------------| +| **Basic** | Yes | No | No | No | No | No | No | +| **Standard** | Yes | Yes | Yes | Yes | Yes | Yes | Yes | + +**Always choose Standard** unless you have an extremely simple single-branch S2S-only scenario. Basic cannot be upgraded to Standard. + +## Hub Components + +A virtual hub is a Microsoft-managed VNet in a specific Azure region. It contains: + +### Gateways + +| Gateway Type | Scale Units | Throughput Per Unit | Max Throughput | +|-------------|-------------|-------------------|---------------| +| S2S VPN Gateway | 1-20 | 500 Mbps | 20 Gbps (with 20 units) | +| P2S VPN Gateway | 1-20 | 500 Mbps | 20 Gbps (with 20 units) | +| ExpressRoute Gateway | 1-10 | 2 Gbps | 10 Gbps (with 10 units) | + +Gateways are deployed on-demand — you only pay for gateways you create. + +```bash +# Create S2S VPN gateway in hub (1 scale unit = 500 Mbps) +az network vpn-gateway create \ + --name \ + --resource-group \ + --vhub \ + --scale-unit 2 + +# Create ExpressRoute gateway in hub (1 scale unit = 2 Gbps) +az network express-route gateway create \ + --name \ + --resource-group \ + --virtual-hub \ + --min-val 1 + +# Create P2S gateway in hub +az network p2s-vpn-gateway create \ + --name \ + --resource-group \ + --vhub \ + --scale-unit 1 \ + --vpn-server-config \ + --address-space 172.16.0.0/24 +``` + +### Hub Address Space + +The hub requires a dedicated CIDR block: +- **Minimum:** /24 +- **Recommended:** /23 (for growth with NVAs and future features) +- Must not overlap with any connected VNet, on-premises range, or other hub ranges +- Cannot be changed after creation + +## Connectivity Models + +### Branch Connectivity (S2S VPN) + +Branches connect to the hub via IPsec/IKE S2S VPN tunnels. vWAN supports automated connectivity from SD-WAN partners. + +```bash +# Create a VPN site (represents a branch) +az network vpn-site create \ + --name \ + --resource-group \ + --virtual-wan \ + --ip-address \ + --address-prefixes 10.1.0.0/16 \ + --device-vendor \ + --device-model + +# Connect VPN site to hub +az network vpn-gateway connection create \ + --name \ + --resource-group \ + --gateway-name \ + --remote-vpn-site \ + --shared-key +``` + +### VNet Connectivity + +VNets connect to hubs as spokes. The connection is a managed resource (not traditional peering). + +```bash +# Connect spoke VNet to hub +az network vhub connection create \ + --name \ + --resource-group \ + --vhub-name \ + --remote-vnet + +# List VNet connections +az network vhub connection list \ + --resource-group \ + --vhub-name +``` + +**Key differences from VNet peering:** +- VNet connections are managed by vWAN routing +- Automatic route propagation between spokes (no UDRs needed) +- Cannot directly apply NSGs on the hub side +- Supports transit: spoke-to-spoke traffic flows through the hub + +### ExpressRoute Connectivity + +ExpressRoute circuits connect to the hub's ExpressRoute gateway. + +```bash +# Connect an ExpressRoute circuit to the hub +az network express-route gateway connection create \ + --name \ + --resource-group \ + --gateway-name \ + --peering \ + --authorization-key # if cross-subscription +``` + +### Remote User Connectivity (P2S) + +P2S VPN connects individual users to the hub, with the same protocol and auth options as standalone VPN Gateway P2S. + +## Transit Connectivity + +### Spoke-to-Spoke Transit + +In Standard vWAN, spoke VNets connected to the **same hub** can communicate through the hub automatically. No additional routing configuration is needed. + +### Inter-Hub Transit + +Spokes connected to **different hubs** in the same vWAN can communicate across hubs. Traffic flows over the Microsoft global backbone between hub regions. + +``` +Spoke A (Hub 1, East US) ──→ Hub 1 ──→ Microsoft Backbone ──→ Hub 2 (West US) ──→ Spoke B +``` + +### Branch-to-VNet Transit + +Branches connected via VPN can reach VNets connected to the same hub (and other hubs) automatically. + +### Branch-to-Branch Transit + +Branches connected to the same hub (or different hubs) can reach each other through the hub routing infrastructure. + +### ExpressRoute-to-VPN Transit + +Traffic from an ExpressRoute-connected site can reach VPN-connected branches through the hub. This requires Standard vWAN and both gateways in the same hub. + +## Hub Routing + +### Default Route Table + +Every hub has a `defaultRouteTable` that receives routes from all connections: +- VNet connection addresses +- VPN site addresses +- ExpressRoute learned routes +- P2S client addresses + +All connections propagate to and associate with the default route table by default. + +### Custom Route Tables + +For advanced routing (e.g., isolating certain spokes), you can create custom route tables: + +```bash +# Create a custom route table +az network vhub route-table create \ + --name IsolatedRT \ + --resource-group \ + --vhub-name + +# Associate a VNet connection with a custom route table +az network vhub connection update \ + --name \ + --resource-group \ + --vhub-name \ + --associated-route-table \ + --propagated-route-tables +``` + +### Effective Routes + +```bash +# View effective routes for the hub +az network vhub get-effective-routes \ + --resource-group \ + --name \ + --resource-type VirtualHub + +# View effective routes for a specific connection +az network vhub get-effective-routes \ + --resource-group \ + --name \ + --resource-type HubVnetConnection \ + --resource-id +``` + +## SD-WAN Partner Integration + +Validated SD-WAN partners can automate branch-to-hub connectivity through the vWAN REST API: + +| Partner | Integration Type | +|---------|-----------------| +| Cisco Viptela / SD-WAN | Automated IPsec tunnels | +| VMware SD-WAN (VeloCloud) | Automated IPsec tunnels | +| Versa Networks | Automated IPsec tunnels | +| Barracuda CloudGen WAN | Automated IPsec tunnels + NVA-in-hub | +| Fortinet FortiGate | NVA-in-hub | +| Check Point CloudGuard | NVA-in-hub | + +Partners automate the creation of VPN sites and connections, eliminating manual configuration for each branch. + +## Migration from Traditional Hub-and-Spoke + +### Planning Considerations + +1. **Inventory all VNets, peerings, and gateways** in the existing topology +2. **Map UDRs** — vWAN replaces most UDRs with automatic routing +3. **Identify NVAs** — determine if they can move to NVA-in-hub or remain in spoke VNets +4. **Plan for downtime** — VNet connections must be removed from the old hub and connected to vWAN + +### Migration Steps + +1. Create the vWAN and hub(s) +2. Create gateways (VPN, ER) in the hub +3. Re-create VPN/ER connections to the hub +4. Disconnect spoke VNets from old hub (remove peerings) +5. Connect spoke VNets to vWAN hub +6. Verify routing and connectivity +7. Decommission old hub VNet and gateways + +## Additional References + +- [About Virtual WAN](https://learn.microsoft.com/azure/virtual-wan/virtual-wan-about) +- [Virtual WAN architecture](https://learn.microsoft.com/azure/virtual-wan/virtual-wan-global-transit-network-architecture) +- [Configure vWAN hub routing](https://learn.microsoft.com/azure/virtual-wan/how-to-virtual-hub-routing) +- [SD-WAN connectivity automation](https://learn.microsoft.com/azure/virtual-wan/virtual-wan-locations-partners) diff --git a/plugin/skills/azure-vnet-manager/SKILL.md b/plugin/skills/azure-vnet-manager/SKILL.md new file mode 100644 index 000000000..9160237cd --- /dev/null +++ b/plugin/skills/azure-vnet-manager/SKILL.md @@ -0,0 +1,204 @@ +--- +name: azure-vnet-manager +description: "Manage Azure virtual networks at scale using Azure Virtual Network Manager (AVNM) for network groups, connectivity configurations (hub-and-spoke, mesh), and security admin rules. WHEN: virtual network manager, AVNM, network groups, connected group, hub and spoke configuration, mesh topology, security admin rules, network manager, manage VNets at scale. DO NOT USE FOR: individual VNet peering (use azure-virtual-network), NSG rules (use azure-virtual-network), Azure Policy for networking (use azure-compliance)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Virtual Network Manager + +Azure Virtual Network Manager (AVNM) is a management service that enables you to group, configure, deploy, and manage virtual networks globally across subscriptions at scale. It replaces the need to manually create and maintain individual peering connections and NSG rules across dozens or hundreds of VNets. + +## When to Use This Skill + +- Grouping virtual networks across multiple subscriptions and regions for centralized management +- Configuring hub-and-spoke topology at scale without manually creating individual peering connections +- Deploying mesh connectivity between VNets so they can communicate directly +- Enforcing security admin rules across all VNets in a network group regardless of individual NSG configurations +- Defining always-allow or always-deny network rules that cannot be overridden by workload teams +- Managing dynamic network group membership using Azure Policy conditions +- Setting up cross-region connectivity with global mesh or hub-and-spoke configurations +- Rolling out connectivity or security changes across many VNets in a controlled deployment workflow + +## Rules + +1. AVNM requires a network manager instance with a defined scope (management group or subscription). The scope determines which VNets the manager can govern. +2. Connectivity and security configurations must be explicitly deployed (committed) to target regions — saving a configuration does not apply it. +3. Security admin rules evaluate before NSG rules. A security admin deny rule blocks traffic even if an NSG allows it. An always-allow security admin rule permits traffic even if an NSG denies it. +4. Network groups can use static membership (manually added VNets) or dynamic membership (Azure Policy conditions). Dynamic groups automatically include VNets matching the conditions. +5. Hub-and-spoke connectivity configurations require the hub VNet to be in the same network manager scope. The hub is not automatically part of the network group. +6. Mesh connectivity creates direct peering between all VNets in a group — consider the peering limit (500 peerings per VNet) when designing large groups. +7. Global mesh and cross-region hub-and-spoke configurations use global VNet peering, which incurs cross-region data transfer charges. +8. Deployments can take several minutes to propagate. Monitor deployment status before assuming changes are active. +9. Removing a VNet from a network group does not automatically delete existing peering connections created by AVNM — you must redeploy the configuration. +10. AVNM is available in most Azure regions but verify availability in your target regions before designing the architecture. + +## MCP Tools + +| Tool | Method | Purpose | +|------|--------|---------| +| — | — | Limited MCP coverage — use CLI commands below | + +> **Note:** Azure Virtual Network Manager has limited MCP server tool coverage. Use Azure CLI commands for all AVNM operations. + +## CLI Fallback + +```bash +# --- Network Manager Instance --- +# Create a network manager +az network manager create \ + --name myNetworkManager \ + --resource-group myRG \ + --location eastus \ + --scope-accesses "Connectivity" "SecurityAdmin" \ + --network-manager-scopes management-groups="/providers/Microsoft.Management/managementGroups/myMG" + +# List network managers +az network manager list --resource-group myRG --output table + +# Show a network manager +az network manager show --name myNetworkManager --resource-group myRG + +# --- Network Groups --- +# Create a static network group +az network manager group create \ + --name myGroup \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --description "Production VNets" + +# Add a VNet to a group (static membership) +az network manager group static-member create \ + --name myStaticMember \ + --network-group-name myGroup \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --resource-id "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworks/myVNet" + +# List static members +az network manager group static-member list \ + --network-group-name myGroup \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# List network groups +az network manager group list \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# --- Connectivity Configurations --- +# Create a hub-and-spoke connectivity configuration +az network manager connect-config create \ + --name myHubSpoke \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --connectivity-topology "HubAndSpoke" \ + --hub resource-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworks/hubVNet" resource-type="Microsoft.Network/virtualNetworks" \ + --applies-to-groups group-connectivity="None" network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/myGroup" use-hub-gateway="True" + +# Create a mesh connectivity configuration +az network manager connect-config create \ + --name myMesh \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --connectivity-topology "Mesh" \ + --applies-to-groups group-connectivity="DirectlyConnected" network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/myGroup" is-global="False" + +# List connectivity configurations +az network manager connect-config list \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# --- Security Admin Configurations --- +# Create a security admin configuration +az network manager security-admin-config create \ + --name mySecurityConfig \ + --network-manager-name myNetworkManager \ + --resource-group myRG + +# Create a rule collection +az network manager security-admin-config rule-collection create \ + --name myRuleCollection \ + --configuration-name mySecurityConfig \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --applies-to-groups network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/myGroup" + +# Create a security admin rule (deny inbound SSH from internet) +az network manager security-admin-config rule-collection rule create \ + --name denySSHFromInternet \ + --rule-collection-name myRuleCollection \ + --configuration-name mySecurityConfig \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --kind "Custom" \ + --protocol "Tcp" \ + --access "Deny" \ + --priority 100 \ + --direction "Inbound" \ + --sources address-prefix="*" address-prefix-type="IPPrefix" \ + --destinations address-prefix="*" address-prefix-type="IPPrefix" \ + --dest-port-ranges 22 + +# List security admin rules +az network manager security-admin-config rule-collection rule list \ + --rule-collection-name myRuleCollection \ + --configuration-name mySecurityConfig \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# --- Deployment --- +# Deploy a connectivity configuration to a region +az network manager post-commit \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --commit-type "Connectivity" \ + --target-locations eastus \ + --configuration-ids "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/connectivityConfigurations/myHubSpoke" + +# Deploy a security admin configuration +az network manager post-commit \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --commit-type "SecurityAdmin" \ + --target-locations eastus \ + --configuration-ids "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/securityAdminConfigurations/mySecurityConfig" + +# List deployments +az network manager list-deploy-status \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --deployment-types "Connectivity" "SecurityAdmin" \ + --regions eastus + +# Delete a network manager +az network manager delete --name myNetworkManager --resource-group myRG --yes +``` + +## Key Concepts + +- **Azure Virtual Network Manager (AVNM)** is a centralized management service for grouping and configuring VNets at scale. It provides two configuration types: connectivity (peering) and security admin rules. +- **Scope** defines what resources the network manager can govern. It can be a management group (for cross-subscription management) or an individual subscription. +- **Network Groups** are logical collections of VNets. They can have static members (manually added) or dynamic members (auto-populated via Azure Policy conditions based on tags, names, or other properties). +- **Connectivity Configurations** define the topology between VNets in a network group. Two topologies are available: hub-and-spoke (star) and mesh (full or partial). +- **Hub-and-Spoke** topology creates peering from each spoke VNet to a central hub VNet. Optional features include direct connectivity between spokes and using the hub as a gateway. +- **Mesh Topology** creates direct peering between all VNets in a group, enabling direct communication without routing through a hub. Global mesh extends this across regions. +- **Security Admin Rules** are centrally managed network security rules that evaluate before NSG rules. They have three access types: Allow, AlwaysAllow, and Deny. +- **Rule Evaluation Order**: Security admin rules evaluate first (by priority), then NSG rules evaluate. An AlwaysAllow security admin rule overrides NSG deny rules. A Deny security admin rule blocks traffic regardless of NSG rules. +- **Deployment (Commit)** is the process of applying configurations to target regions. Changes to configurations are not effective until they are committed and deployed. +- **Configuration Drift** — AVNM continuously enforces deployed configurations. If someone manually deletes an AVNM-managed peering, it is automatically recreated. + +## References + +- [Network groups and membership](references/network-groups.md) +- [Connectivity configurations — hub-and-spoke and mesh](references/connectivity-configs.md) +- [Security admin rules](references/security-admin-rules.md) +- [Deployment workflow](references/deployment.md) +- [Azure Virtual Network Manager overview — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/overview) +- [Azure Virtual Network Manager FAQ — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/faq) diff --git a/plugin/skills/azure-vnet-manager/references/connectivity-configs.md b/plugin/skills/azure-vnet-manager/references/connectivity-configs.md new file mode 100644 index 000000000..b51062676 --- /dev/null +++ b/plugin/skills/azure-vnet-manager/references/connectivity-configs.md @@ -0,0 +1,172 @@ +# Connectivity Configurations + +Connectivity configurations in Azure Virtual Network Manager (AVNM) define the topology between VNets in a network group. They automate peering creation and ongoing management, replacing manual VNet peering at scale. + +## Topology Types + +AVNM supports two connectivity topologies: + +| Topology | Description | Peering Pattern | +|----------|-------------|-----------------| +| **Hub-and-spoke** | Spoke VNets peer to a central hub VNet | Star topology — spokes connect to hub, not to each other (unless direct connectivity is enabled) | +| **Mesh** | All VNets in the group peer directly with each other | Full mesh — every VNet can communicate directly with every other VNet | + +## Hub-and-Spoke Topology + +Hub-and-spoke is the most common enterprise topology. Spoke VNets route traffic through a central hub for shared services (firewall, VPN gateway, DNS). + +### Creating a hub-and-spoke configuration + +```bash +az network manager connect-config create \ + --name hubSpokeConfig \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --connectivity-topology "HubAndSpoke" \ + --hub resource-id="/subscriptions/{sub}/resourceGroups/hubRG/providers/Microsoft.Network/virtualNetworks/hubVNet" resource-type="Microsoft.Network/virtualNetworks" \ + --applies-to-groups group-connectivity="None" network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/spokeGroup" use-hub-gateway="False" +``` + +### Hub requirements + +- The hub VNet must be within the network manager's scope +- The hub VNet does not need to be in the network group — it is specified separately in the configuration +- Only one hub VNet per hub-and-spoke configuration + +### Direct connectivity between spokes + +By default, spoke VNets communicate through the hub. Enable direct connectivity to allow spokes to reach each other without transiting the hub: + +```bash +az network manager connect-config create \ + --name hubSpokeWithDirect \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --connectivity-topology "HubAndSpoke" \ + --hub resource-id="/subscriptions/{sub}/resourceGroups/hubRG/providers/Microsoft.Network/virtualNetworks/hubVNet" resource-type="Microsoft.Network/virtualNetworks" \ + --applies-to-groups group-connectivity="DirectlyConnected" network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/spokeGroup" use-hub-gateway="False" +``` + +Setting `group-connectivity="DirectlyConnected"` creates peering between all spokes in the group in addition to the hub-to-spoke peering. + +### Using the hub as a gateway + +If the hub VNet has a VPN or ExpressRoute gateway, spokes can use it for on-premises connectivity: + +```bash +az network manager connect-config create \ + --name hubSpokeWithGateway \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --connectivity-topology "HubAndSpoke" \ + --hub resource-id="/subscriptions/{sub}/resourceGroups/hubRG/providers/Microsoft.Network/virtualNetworks/hubVNet" resource-type="Microsoft.Network/virtualNetworks" \ + --applies-to-groups group-connectivity="None" network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/spokeGroup" use-hub-gateway="True" +``` + +This sets `useRemoteGateways=true` on the spoke peering and `allowGatewayTransit=true` on the hub peering, equivalent to manual gateway transit configuration. + +## Mesh Topology + +Mesh topology creates direct peering between all VNets in a group. Every VNet can communicate with every other VNet without routing through a hub. + +### Creating a mesh configuration + +```bash +az network manager connect-config create \ + --name meshConfig \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --connectivity-topology "Mesh" \ + --applies-to-groups group-connectivity="DirectlyConnected" network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/appGroup" is-global="False" +``` + +### Regional vs global mesh + +| Setting | Description | Cost | +|---------|-------------|------| +| `is-global="False"` | Mesh only between VNets in the same region | Regional peering rates (often free for data transfer within a region) | +| `is-global="True"` | Mesh across all regions | Global peering rates (cross-region data transfer charges apply) | + +```bash +# Create a global mesh +az network manager connect-config create \ + --name globalMesh \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --connectivity-topology "Mesh" \ + --applies-to-groups group-connectivity="DirectlyConnected" network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/appGroup" is-global="True" +``` + +### When to use mesh + +- All VNets need direct communication (no shared services hub needed) +- Workloads require low-latency direct paths between VNets +- Small to medium number of VNets (consider peering limits) +- Microservices or distributed applications spread across VNets + +## Choosing Between Topologies + +| Factor | Hub-and-Spoke | Mesh | +|--------|---------------|------| +| Central firewall/NVA | Yes — route through hub | No central point (must use security admin rules or per-VNet NVAs) | +| Shared VPN/ER gateway | Yes — use hub gateway | No shared gateway | +| VNet-to-VNet latency | Higher (transits hub) | Lower (direct peering) | +| Scalability | Very high (spokes only peer to hub) | Limited by peering count (500 peerings per VNet) | +| Management complexity | Lower | Higher for large groups | +| Spoke-to-spoke communication | Requires NVA/firewall or direct connectivity option | Built-in | + +## Multiple Network Groups + +A connectivity configuration can reference multiple network groups. Different groups can have different settings: + +```bash +# Hub-and-spoke with two spoke groups, one with direct connectivity +az network manager connect-config create \ + --name multiGroupConfig \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --connectivity-topology "HubAndSpoke" \ + --hub resource-id="/subscriptions/{sub}/resourceGroups/hubRG/providers/Microsoft.Network/virtualNetworks/hubVNet" resource-type="Microsoft.Network/virtualNetworks" \ + --applies-to-groups \ + group-connectivity="DirectlyConnected" network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/webGroup" use-hub-gateway="False" \ + group-connectivity="None" network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/dataGroup" use-hub-gateway="True" +``` + +In this example, web VNets can communicate directly, while data VNets only route through the hub and use its gateway. + +## Managing Connectivity Configurations + +```bash +# List connectivity configurations +az network manager connect-config list \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# Show a specific configuration +az network manager connect-config show \ + --name hubSpokeConfig \ + --network-manager-name myNetworkManager \ + --resource-group myRG + +# Delete a configuration (must not be deployed) +az network manager connect-config delete \ + --name hubSpokeConfig \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --yes +``` + +## Important Considerations + +1. **Peering limits** — each VNet supports up to 500 peerings. In a mesh with N VNets, each VNet has N-1 peerings. A group of 100 VNets means 99 peerings per VNet. +2. **Address space overlap** — VNets with overlapping address spaces cannot be peered. AVNM will fail to deploy if overlaps exist. +3. **Existing peerings** — AVNM-managed peerings coexist with manually created peerings. If a manual peering already exists between two VNets, AVNM takes over management of that peering. +4. **Configuration changes require redeployment** — modifying a configuration does not automatically apply changes. You must commit and deploy again. +5. **One connectivity configuration per VNet** — a VNet should not be a spoke in two different hub-and-spoke configurations simultaneously. + +## Learn More + +- [Connectivity configuration overview — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/concept-connectivity-configuration) +- [Create a hub-and-spoke topology — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/how-to-create-hub-and-spoke) +- [Create a mesh topology — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/how-to-create-mesh-network-topology) diff --git a/plugin/skills/azure-vnet-manager/references/deployment.md b/plugin/skills/azure-vnet-manager/references/deployment.md new file mode 100644 index 000000000..eb8181dc6 --- /dev/null +++ b/plugin/skills/azure-vnet-manager/references/deployment.md @@ -0,0 +1,194 @@ +# Deployment + +Azure Virtual Network Manager configurations must be explicitly deployed (committed) to take effect. Saving a connectivity configuration or security admin rule does not apply it to the network — you must commit and deploy to target regions. + +## Deployment Workflow + +The deployment process follows these steps: + +1. **Author** — create or modify configurations (connectivity, security admin rules) +2. **Commit** — submit the configuration for deployment to specific regions +3. **Deploy** — AVNM applies the configuration to VNets in the target regions +4. **Monitor** — verify deployment status and check for errors +5. **Enforce** — AVNM continuously enforces the configuration (re-applies if drifted) + +## Deploying Configurations + +### Deploy a connectivity configuration + +```bash +az network manager post-commit \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --commit-type "Connectivity" \ + --target-locations eastus westus2 \ + --configuration-ids "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/connectivityConfigurations/hubSpokeConfig" +``` + +### Deploy a security admin configuration + +```bash +az network manager post-commit \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --commit-type "SecurityAdmin" \ + --target-locations eastus westus2 \ + --configuration-ids "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/securityAdminConfigurations/baselineSecurity" +``` + +### Deploy multiple configurations at once + +You can deploy multiple configurations of the same type in one commit: + +```bash +az network manager post-commit \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --commit-type "Connectivity" \ + --target-locations eastus \ + --configuration-ids \ + "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/connectivityConfigurations/hubSpokeConfig" \ + "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/connectivityConfigurations/meshConfig" +``` + +> **Note:** Connectivity and security admin configurations must be deployed separately — each `post-commit` call handles one commit type. + +## Deployment Regions + +- You must specify which regions to deploy to using `--target-locations` +- Only VNets located in the specified regions receive the configuration +- You can deploy the same configuration to different regions in separate commits +- If your VNets span 5 regions, you need to include all 5 regions in the target-locations + +### Determine which regions to target + +```bash +# List VNets in a network group to identify their regions +az network manager group static-member list \ + --network-group-name myGroup \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# Then check VNet locations +az network vnet list --output table --query "[].{Name:name, Location:location, RG:resourceGroup}" +``` + +## Monitoring Deployment Status + +### Check deployment status + +```bash +az network manager list-deploy-status \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --deployment-types "Connectivity" "SecurityAdmin" \ + --regions eastus westus2 +``` + +### Deployment status values + +| Status | Description | +|--------|-------------| +| `Deploying` | Configuration is being applied to VNets in the region | +| `Deployed` | Configuration is active and enforced | +| `Failed` | Deployment encountered an error — check error details | +| `NotStarted` | Deployment has not begun for this region | + +### Common deployment errors + +| Error | Cause | Resolution | +|-------|-------|------------| +| Address space overlap | Two VNets in a mesh group have overlapping address ranges | Remove one VNet from the group or fix the address space | +| Hub VNet not found | Hub VNet was deleted or moved | Update the connectivity configuration with a valid hub | +| Insufficient permissions | Network manager identity lacks rights on target subscription | Grant Network Contributor role on the subscription | +| Peering limit exceeded | A VNet would exceed 500 peerings | Reduce the network group size or switch from mesh to hub-and-spoke | +| VNet in multiple hub-and-spoke configs | A VNet is a spoke in two configurations deployed to the same region | Remove the VNet from one configuration | + +## Updating Deployed Configurations + +To update a deployed configuration: + +1. Modify the configuration (add/remove rules, change topology settings) +2. Commit and deploy again to the same regions + +```bash +# After modifying a connectivity configuration, redeploy +az network manager post-commit \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --commit-type "Connectivity" \ + --target-locations eastus \ + --configuration-ids "/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/connectivityConfigurations/hubSpokeConfig" +``` + +AVNM performs an incremental update — it only changes what is different from the current deployed state. + +## Removing a Deployed Configuration + +To undeploy a configuration from a region, deploy to the region with an empty configuration list: + +```bash +# Remove all connectivity configurations from eastus +az network manager post-commit \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --commit-type "Connectivity" \ + --target-locations eastus \ + --configuration-ids "" +``` + +This removes AVNM-managed peerings and security admin rules from the VNets in that region. + +> **Warning:** Undeploying a connectivity configuration removes the peerings it created. VNets that relied on those peerings will lose connectivity. Plan maintenance windows accordingly. + +## Rollback Considerations + +AVNM does not have a built-in rollback feature. To revert a change: + +1. **Re-deploy the previous configuration** — if you saved the previous configuration, modify the current one to match the previous state and redeploy +2. **Undeploy the configuration** — remove the configuration from the region entirely +3. **Keep configuration versions** — maintain documentation or naming conventions for configuration versions (e.g., `hubSpokeConfig-v1`, `hubSpokeConfig-v2`) + +### Rollback strategies + +| Strategy | How | When | +|----------|-----|------| +| Re-deploy previous state | Modify configuration back to previous settings, then commit | Minor changes that can be easily reversed | +| Undeploy | Deploy with empty configuration IDs | Emergency — need to remove all AVNM management immediately | +| Blue-green configurations | Maintain two configurations, deploy the active one | Major topology changes — deploy new config, verify, undeploy old | + +## Configuration Drift Protection + +AVNM continuously enforces deployed configurations: + +- If someone manually deletes an AVNM-managed peering, AVNM recreates it +- If someone modifies peering settings managed by AVNM, AVNM reverts them +- If a new VNet joins a dynamic network group, AVNM automatically configures it on the next enforcement cycle + +Drift correction happens within minutes but is not instantaneous. During the window between manual change and drift correction, connectivity may be affected. + +## Deployment Best Practices + +1. **Deploy to non-production first** — test configurations in dev/staging regions before deploying to production regions +2. **Deploy one region at a time** — for critical changes, deploy to one region, verify, then expand to other regions +3. **Monitor after deployment** — check deployment status and verify VNet connectivity after each deployment +4. **Document configurations** — AVNM does not version configurations; maintain your own change log +5. **Use separate configurations for different environments** — avoid mixing dev, staging, and production VNets in the same configuration +6. **Schedule maintenance windows for undeploy operations** — undeploying connectivity configurations disrupts traffic + +## Quotas and Limits + +| Resource | Limit | +|----------|-------| +| Connectivity configurations per network manager | 20 | +| Security admin configurations per network manager | 20 | +| Rule collections per security admin configuration | 10 | +| Rules per rule collection | 100 | +| Deployments (concurrent) | Limited by Azure resource manager throttling | + +## Learn More + +- [Deployment overview — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/concept-deployments) +- [Deploy configurations — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/how-to-deploy-configurations) +- [Remove deployed configurations — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/how-to-remove-deployed-configurations) diff --git a/plugin/skills/azure-vnet-manager/references/network-groups.md b/plugin/skills/azure-vnet-manager/references/network-groups.md new file mode 100644 index 000000000..b6a90fec4 --- /dev/null +++ b/plugin/skills/azure-vnet-manager/references/network-groups.md @@ -0,0 +1,221 @@ +# Network Groups + +Network groups are the foundational building block of Azure Virtual Network Manager (AVNM). A network group is a logical collection of virtual networks that you manage together — applying connectivity configurations, security admin rules, and governance policies to all members at once. + +## Membership Types + +AVNM supports two membership models that can be used together in the same group: + +| Type | How Members Are Added | Best For | +|------|----------------------|----------| +| **Static** | Manually add individual VNets by resource ID | Small, stable sets of VNets with well-known identities | +| **Dynamic** | Azure Policy conditions auto-add matching VNets | Large or growing environments where VNets are tagged/named consistently | + +## Static Membership + +Static membership means you explicitly add each VNet to the group. + +```bash +# Create a network group +az network manager group create \ + --name prodVNets \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --description "Production virtual networks" + +# Add a VNet as a static member +az network manager group static-member create \ + --name vnet1-member \ + --network-group-name prodVNets \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --resource-id "/subscriptions/{sub}/resourceGroups/appRG/providers/Microsoft.Network/virtualNetworks/appVNet" + +# Add another VNet +az network manager group static-member create \ + --name vnet2-member \ + --network-group-name prodVNets \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --resource-id "/subscriptions/{sub2}/resourceGroups/dbRG/providers/Microsoft.Network/virtualNetworks/dbVNet" + +# List static members +az network manager group static-member list \ + --network-group-name prodVNets \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# Remove a static member +az network manager group static-member delete \ + --name vnet1-member \ + --network-group-name prodVNets \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --yes +``` + +### When to use static membership + +- You have a small, known set of VNets (under 20) +- VNets don't follow a consistent tagging or naming convention +- You need precise control over which VNets are in each group +- Testing configurations before rolling out dynamic membership + +## Dynamic Membership + +Dynamic membership uses Azure Policy conditions to automatically include VNets that match specified criteria. When a new VNet is created that matches the conditions, it is automatically added to the group. + +### Condition-based matching + +Dynamic membership conditions evaluate VNet properties. Common conditions: + +| Property | Operator | Example | +|----------|----------|---------| +| `Name` | Contains, Equals, NotContains | Name contains "prod" | +| `Tags` | Exists, Equals | Tag "environment" equals "production" | +| `Resource Group` | Equals, Contains | Resource group contains "app" | +| `Subscription` | Equals | Specific subscription ID | +| `Type` | Equals | Microsoft.Network/virtualNetworks | + +### Creating dynamic membership via Azure Policy + +Dynamic group membership is defined through Azure Policy definitions applied to the network manager's scope. The policy evaluates VNet resources and assigns matching ones to the group. + +Example: Auto-add all VNets tagged with `environment=production`: + +The policy condition in the network group definition: + +```json +{ + "allOf": [ + { + "field": "type", + "equals": "Microsoft.Network/virtualNetworks" + }, + { + "field": "tags['environment']", + "equals": "production" + } + ] +} +``` + +Example: Auto-add VNets whose name starts with "spoke-": + +```json +{ + "allOf": [ + { + "field": "type", + "equals": "Microsoft.Network/virtualNetworks" + }, + { + "field": "name", + "like": "spoke-*" + } + ] +} +``` + +Example: Auto-add VNets in specific subscriptions: + +```json +{ + "allOf": [ + { + "field": "type", + "equals": "Microsoft.Network/virtualNetworks" + }, + { + "field": "Microsoft.Network/virtualNetworks/subscriptionId", + "in": [ + "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "ffffffff-gggg-hhhh-iiii-jjjjjjjjjjjj" + ] + } + ] +} +``` + +### When to use dynamic membership + +- Large environments with dozens or hundreds of VNets +- VNets are created and destroyed frequently +- Consistent tagging strategy is in place +- You want "zero-touch" group management — new VNets auto-enroll + +## Cross-Subscription Groups + +AVNM can manage VNets across multiple subscriptions when the network manager's scope is set to a management group. + +```bash +# Create a network manager scoped to a management group +az network manager create \ + --name crossSubManager \ + --resource-group centralRG \ + --location eastus \ + --scope-accesses "Connectivity" "SecurityAdmin" \ + --network-manager-scopes management-groups="/providers/Microsoft.Management/managementGroups/myMG" +``` + +Requirements for cross-subscription groups: +- The network manager scope must include the subscriptions containing the VNets +- The identity running the network manager needs `Network Contributor` or equivalent on the target subscriptions +- Dynamic membership policies must be assigned at the management group level to evaluate VNets across subscriptions + +## Combining Static and Dynamic Membership + +A single network group can use both static and dynamic membership simultaneously. This is useful when: + +- Most VNets are tagged and auto-join via dynamic membership +- A few VNets don't follow the tagging convention and need static addition +- You want to test a VNet in the group before updating its tags + +Members added both statically and dynamically appear once — there is no duplication. + +## Managing Network Groups + +```bash +# List all network groups +az network manager group list \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# Show a specific group +az network manager group show \ + --name prodVNets \ + --network-manager-name myNetworkManager \ + --resource-group myRG + +# Delete a network group (must not be referenced by configurations) +az network manager group delete \ + --name prodVNets \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --yes +``` + +## Limits + +| Resource | Limit | +|----------|-------| +| Network groups per network manager | 100 | +| Static members per network group | 1,000 | +| VNets managed per network manager | 1,000 (can be increased) | +| Network managers per subscription | 5 | + +## Best Practices + +1. **Use tags consistently** — establish a tagging convention (e.g., `environment`, `team`, `network-tier`) and enforce it with Azure Policy so dynamic membership works reliably. +2. **Start with static, graduate to dynamic** — use static membership during initial setup and testing, then move to dynamic once tagging is standardized. +3. **Avoid overlapping group conditions** — if a VNet matches multiple groups with conflicting configurations, deployment behavior may be unpredictable. +4. **Monitor group membership** — periodically review group members to ensure dynamic conditions are not over-matching or under-matching. +5. **Use management group scope for enterprise** — scoping to a management group enables cross-subscription management from a single network manager. + +## Learn More + +- [Network groups overview — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/concept-network-groups) +- [Define dynamic network group membership — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/concept-azure-policy-integration) +- [Create a network group — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/how-to-create-network-group) diff --git a/plugin/skills/azure-vnet-manager/references/security-admin-rules.md b/plugin/skills/azure-vnet-manager/references/security-admin-rules.md new file mode 100644 index 000000000..3624feced --- /dev/null +++ b/plugin/skills/azure-vnet-manager/references/security-admin-rules.md @@ -0,0 +1,251 @@ +# Security Admin Rules + +Security admin rules in Azure Virtual Network Manager (AVNM) provide centralized, top-down network security enforcement across all VNets in a network group. They evaluate before NSG rules, enabling platform teams to enforce security baselines that workload teams cannot override. + +## Security Admin Rules vs NSGs + +| Feature | Security Admin Rules | Network Security Groups | +|---------|---------------------|------------------------| +| Scope | All VNets in a network group | Individual subnet or NIC | +| Management | Central platform team | Workload team or resource owner | +| Evaluation order | First (before NSGs) | Second (after security admin rules) | +| Override by workload teams | No (except AllowAlways allows NSG to evaluate) | Yes (workload teams control NSG rules) | +| Access types | Allow, AlwaysAllow, Deny | Allow, Deny | +| Maximum rules | 100 per rule collection | 1,000 per NSG | + +## Rule Evaluation Order + +Traffic evaluation follows this sequence: + +1. **Security admin rules** (managed by AVNM) — evaluated first, in priority order (lower number = higher priority) +2. **NSG rules** — evaluated second, in priority order + +### How access types interact with NSGs + +| Security Admin Rule | NSG Rule | Final Result | +|--------------------|----------|--------------| +| **Deny** | Allow | **Denied** — security admin deny overrides NSG allow | +| **Deny** | Deny | **Denied** | +| **Allow** | Allow | **Allowed** | +| **Allow** | Deny | **Denied** — security admin allow passes to NSG, which denies | +| **AlwaysAllow** | Allow | **Allowed** | +| **AlwaysAllow** | Deny | **Allowed** — AlwaysAllow overrides NSG deny | +| No matching rule | Allow | **Allowed** — falls through to NSG evaluation | +| No matching rule | Deny | **Denied** — falls through to NSG evaluation | + +Key insight: +- **Deny**: Blocks traffic regardless of NSGs. Use for hard security boundaries. +- **Allow**: Permits traffic to continue to NSG evaluation. NSGs can still deny it. Use when you want to set a baseline but let workload teams add further restrictions. +- **AlwaysAllow**: Permits traffic regardless of NSGs. Use for traffic that must always flow (e.g., monitoring probes, management traffic). + +## Creating Security Admin Rules + +### Step 1: Create a security admin configuration + +```bash +az network manager security-admin-config create \ + --name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG +``` + +### Step 2: Create a rule collection + +A rule collection groups rules together and associates them with network groups: + +```bash +az network manager security-admin-config rule-collection create \ + --name prodRules \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --applies-to-groups network-group-id="/subscriptions/{sub}/resourceGroups/myRG/providers/Microsoft.Network/networkManagers/myNetworkManager/networkGroups/prodVNets" +``` + +### Step 3: Create rules + +#### Deny inbound SSH from the internet + +```bash +az network manager security-admin-config rule-collection rule create \ + --name denySSHFromInternet \ + --rule-collection-name prodRules \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --kind "Custom" \ + --protocol "Tcp" \ + --access "Deny" \ + --priority 100 \ + --direction "Inbound" \ + --sources address-prefix="Internet" address-prefix-type="ServiceTag" \ + --destinations address-prefix="*" address-prefix-type="IPPrefix" \ + --dest-port-ranges 22 +``` + +#### Deny inbound RDP from the internet + +```bash +az network manager security-admin-config rule-collection rule create \ + --name denyRDPFromInternet \ + --rule-collection-name prodRules \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --kind "Custom" \ + --protocol "Tcp" \ + --access "Deny" \ + --priority 110 \ + --direction "Inbound" \ + --sources address-prefix="Internet" address-prefix-type="ServiceTag" \ + --destinations address-prefix="*" address-prefix-type="IPPrefix" \ + --dest-port-ranges 3389 +``` + +#### Always allow Azure Load Balancer health probes + +```bash +az network manager security-admin-config rule-collection rule create \ + --name alwaysAllowLBProbes \ + --rule-collection-name prodRules \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --kind "Custom" \ + --protocol "*" \ + --access "AlwaysAllow" \ + --priority 200 \ + --direction "Inbound" \ + --sources address-prefix="AzureLoadBalancer" address-prefix-type="ServiceTag" \ + --destinations address-prefix="*" address-prefix-type="IPPrefix" \ + --dest-port-ranges 0-65535 +``` + +#### Deny all outbound to a known bad IP range + +```bash +az network manager security-admin-config rule-collection rule create \ + --name denyBadRange \ + --rule-collection-name prodRules \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --kind "Custom" \ + --protocol "*" \ + --access "Deny" \ + --priority 300 \ + --direction "Outbound" \ + --sources address-prefix="*" address-prefix-type="IPPrefix" \ + --destinations address-prefix="198.51.100.0/24" address-prefix-type="IPPrefix" \ + --dest-port-ranges 0-65535 +``` + +#### Allow management traffic from a jump box subnet + +```bash +az network manager security-admin-config rule-collection rule create \ + --name allowManagement \ + --rule-collection-name prodRules \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --kind "Custom" \ + --protocol "Tcp" \ + --access "AlwaysAllow" \ + --priority 150 \ + --direction "Inbound" \ + --sources address-prefix="10.0.255.0/24" address-prefix-type="IPPrefix" \ + --destinations address-prefix="*" address-prefix-type="IPPrefix" \ + --dest-port-ranges 22 3389 +``` + +## Rule Priority + +- Priority range: 1–4096 (lower number = higher priority, evaluated first) +- Rules within a rule collection are evaluated in priority order +- When a rule matches, the action is taken and no further rules in that collection are evaluated +- If no rule matches, traffic passes to NSG evaluation + +## Source and Destination Address Types + +| Type | Description | Example | +|------|-------------|---------| +| `IPPrefix` | IP address or CIDR range | `10.0.0.0/8`, `*` (any) | +| `ServiceTag` | Azure service tag | `Internet`, `AzureLoadBalancer`, `VirtualNetwork` | + +## Managing Security Admin Rules + +```bash +# List rule collections +az network manager security-admin-config rule-collection list \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# List rules in a collection +az network manager security-admin-config rule-collection rule list \ + --rule-collection-name prodRules \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --output table + +# Delete a rule +az network manager security-admin-config rule-collection rule delete \ + --name denyBadRange \ + --rule-collection-name prodRules \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --yes + +# Delete a rule collection +az network manager security-admin-config rule-collection delete \ + --name prodRules \ + --configuration-name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --yes + +# Delete a security admin configuration (must not be deployed) +az network manager security-admin-config delete \ + --name baselineSecurity \ + --network-manager-name myNetworkManager \ + --resource-group myRG \ + --yes +``` + +## Common Security Baselines + +### Enterprise baseline (recommended starting point) + +| Priority | Direction | Rule | Access | Purpose | +|----------|-----------|------|--------|---------| +| 100 | Inbound | Deny SSH from Internet | Deny | Block SSH from public internet | +| 110 | Inbound | Deny RDP from Internet | Deny | Block RDP from public internet | +| 200 | Inbound | AlwaysAllow LB probes | AlwaysAllow | Ensure health probes always work | +| 210 | Inbound | AlwaysAllow monitoring | AlwaysAllow | Ensure Azure Monitor agent traffic flows | +| 300 | Inbound | Allow management from jump box | AlwaysAllow | Bastion/jump box access for admins | + +### High-security baseline (add to enterprise baseline) + +| Priority | Direction | Rule | Access | Purpose | +|----------|-----------|------|--------|---------| +| 120 | Inbound | Deny high-risk ports | Deny | Block SMB (445), Telnet (23), FTP (21) from internet | +| 310 | Outbound | Deny known bad IPs | Deny | Block outbound to threat intel IP ranges | +| 320 | Outbound | Deny IRC | Deny | Block IRC (6667) used by botnets | + +## Limitations + +- Maximum 100 security admin rules per rule collection +- Maximum 10 rule collections per security admin configuration +- Service tags are supported as sources and destinations, but not all service tags are available +- Security admin rules cannot reference application security groups (ASGs) +- Changes require redeployment to take effect + +## Learn More + +- [Security admin rules overview — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/concept-security-admins) +- [Create security admin rules — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/how-to-block-network-traffic-portal) +- [Security admin rules vs NSGs — Microsoft Learn](https://learn.microsoft.com/azure/virtual-network-manager/concept-security-admins#security-admin-rules-versus-network-security-groups) diff --git a/plugin/skills/azure-vpn-gateway/SKILL.md b/plugin/skills/azure-vpn-gateway/SKILL.md new file mode 100644 index 000000000..f552a155a --- /dev/null +++ b/plugin/skills/azure-vpn-gateway/SKILL.md @@ -0,0 +1,115 @@ +--- +name: azure-vpn-gateway +description: "Provision and manage Azure VPN Gateways for encrypted hybrid connectivity including site-to-site (S2S), point-to-site (P2S), and VNet-to-VNet tunnels with IPsec/IKE, BGP, and active-active high availability. WHEN: VPN gateway, site-to-site VPN, point-to-site, P2S VPN, S2S VPN, IPsec tunnel, VNet-to-VNet, VPN connection, on-premises VPN, IKE, GatewaySubnet. DO NOT USE FOR: private dedicated connectivity (use azure-expressroute), managed hub-and-spoke (use azure-virtual-wan), DNS-based routing (use azure-traffic-manager)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure VPN Gateway + +## When to Use This Skill + +- Creating or managing site-to-site (S2S) VPN connections between on-premises networks and Azure VNets +- Configuring point-to-site (P2S) VPN for remote user access to Azure resources +- Setting up VNet-to-VNet encrypted tunnels across regions or subscriptions +- Configuring BGP for dynamic routing over VPN connections +- Designing active-active VPN gateways for high availability +- Selecting VPN Gateway SKUs based on throughput and tunnel count requirements +- Configuring custom IPsec/IKE policies for compliance or interoperability +- Troubleshooting VPN tunnel connectivity, IKE negotiation failures, or throughput issues +- Coexisting VPN Gateway with ExpressRoute on the same VNet (see azure-expressroute) + +## Rules + +1. **GatewaySubnet is mandatory.** Always create a subnet named exactly `GatewaySubnet` in the VNet before deploying a VPN gateway. Recommended size is /27 or larger. +2. **Route-based is the default recommendation.** Use route-based VPN type for most scenarios. Policy-based VPN is limited to a single S2S tunnel and no P2S support. +3. **SKU determines capacity.** VpnGw1 supports up to 30 S2S tunnels and 250 Mbps benchmark throughput. VpnGw5 supports up to 100 tunnels and 10 Gbps. Always validate SKU limits before committing. +4. **Zone-redundant SKUs for production.** Use VpnGw1AZ through VpnGw5AZ in regions that support availability zones for zone-redundant deployments. +5. **Shared key management.** Never output or log the shared key (PSK) in plaintext. Store in Azure Key Vault and reference securely. +6. **BGP requires unique ASNs.** Azure defaults to ASN 65515. On-premises devices must use a different ASN. Do not use reserved ASNs (0, 65515 in some cases, 4294967295). +7. **Active-active needs two public IPs.** Each gateway instance requires its own public IP address. BGP is mandatory for active-active configurations. +8. **Gateway provisioning takes 30-45 minutes.** Always warn users about deployment time. Do not suggest gateway creation in tight change windows. +9. **Coexistence with ExpressRoute.** VPN and ExpressRoute gateways can coexist on the same VNet using separate GatewaySubnet IPs. VPN acts as failover path. +10. **P2S protocol selection matters.** OpenVPN supports all client OS platforms. IKEv2 is best for macOS. SSTP is Windows-only and limited to TCP 443. + +## MCP Tools + +| Tool | Operation | Purpose | +|------|-----------|---------| +| `azure__network` | `vpn_gateway_list` | List all VPN gateways in a subscription or resource group | +| `azure__network` | `vpn_connection_list` | List all VPN connections associated with a gateway | + +## CLI Fallback + +```bash +# List VPN gateways +az network vnet-gateway list --resource-group + +# Show VPN gateway details +az network vnet-gateway show --name --resource-group + +# Create a route-based VPN gateway +az network vnet-gateway create \ + --name \ + --resource-group \ + --vnet \ + --gateway-type Vpn \ + --vpn-type RouteBased \ + --sku VpnGw2AZ \ + --public-ip-addresses \ + --no-wait + +# Create a local network gateway (on-prem representation) +az network local-gateway create \ + --name \ + --resource-group \ + --gateway-ip-address \ + --local-address-prefixes + +# Create S2S VPN connection +az network vpn-connection create \ + --name \ + --resource-group \ + --vnet-gateway1 \ + --local-gateway2 \ + --shared-key + +# List VPN connections +az network vpn-connection list --resource-group + +# Show connection status +az network vpn-connection show --name --resource-group + +# Reset a VPN gateway +az network vnet-gateway reset --name --resource-group + +# Download P2S VPN client configuration +az network vnet-gateway vpn-client generate \ + --name \ + --resource-group \ + --processor-architecture Amd64 +``` + +## Key Concepts + +- **VPN types:** Route-based (dynamic routing, supports P2S, multi-site, VNet-to-VNet, coexistence with ExpressRoute) vs Policy-based (static routing, single S2S tunnel only). +- **Gateway SKUs:** VpnGw1 (650 Mbps) → VpnGw2 (1 Gbps) → VpnGw3 (1.25 Gbps) → VpnGw4 (5 Gbps) → VpnGw5 (10 Gbps). AZ variants add zone-redundancy. Basic SKU is legacy — avoid for production. +- **GatewaySubnet:** Dedicated subnet for gateway VMs. /27 recommended, /28 minimum. No NSGs or UDRs on this subnet unless specifically documented. +- **Connection types:** IPsec (S2S to on-prem), Vnet2Vnet (encrypted cross-VNet), ExpressRoute (dedicated circuit gateway binding). +- **BGP:** Border Gateway Protocol for dynamic route exchange. Eliminates static route maintenance. Required for active-active, transit routing, and multi-site with overlapping address spaces. +- **Active-active:** Two gateway instances with two public IPs and two tunnels per on-prem device. Provides automatic failover with minimal downtime. +- **IPsec/IKE:** Phase 1 (IKE SA) establishes secure channel; Phase 2 (IPsec SA) creates the data tunnel. Default parameters work for most scenarios, but custom policies are available for compliance. +- **VPN coexistence with ExpressRoute:** S2S VPN can serve as a backup path when ExpressRoute goes down. Requires both gateway types in the same VNet. +- **NAT rules:** VPN Gateway supports NAT (IngressSnat, EgressSnat) to handle overlapping address spaces between connected networks. + +## References + +- [references/vpn-types.md](references/vpn-types.md) — VPN types, gateway SKUs, throughput benchmarks +- [references/ipsec-ike-params.md](references/ipsec-ike-params.md) — IPsec/IKE default and custom policy parameters +- [references/s2s-config.md](references/s2s-config.md) — Site-to-site VPN setup checklist and troubleshooting +- [references/p2s-config.md](references/p2s-config.md) — Point-to-site VPN configuration options +- [references/active-active.md](references/active-active.md) — Active-active gateway design and BGP +- [Azure VPN Gateway documentation](https://learn.microsoft.com/azure/vpn-gateway/) +- [VPN Gateway FAQ](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-vpn-faq) diff --git a/plugin/skills/azure-vpn-gateway/references/active-active.md b/plugin/skills/azure-vpn-gateway/references/active-active.md new file mode 100644 index 000000000..cdafa76e0 --- /dev/null +++ b/plugin/skills/azure-vpn-gateway/references/active-active.md @@ -0,0 +1,240 @@ +# Active-Active VPN Gateway + +## Overview + +An active-active VPN gateway deploys two gateway instances, each with its own public IP address. This provides redundancy and higher aggregate throughput through parallel tunnels. Active-active is the recommended configuration for production workloads. + +## Architecture + +``` +On-Premises VPN Device +├── Tunnel 1 → Azure GW Instance 0 (Public IP 1) +└── Tunnel 2 → Azure GW Instance 1 (Public IP 2) +``` + +In active-active mode: +- Both gateway instances are active simultaneously +- Each instance has its own public IP +- Both tunnels carry traffic (ECMP load balancing with BGP) +- If one instance fails, the other continues without interruption + +For **dual-redundancy** (active-active on both sides): + +``` +On-Prem Device 1 ─── Tunnel 1 ──→ Azure GW Instance 0 +On-Prem Device 1 ─── Tunnel 2 ──→ Azure GW Instance 1 +On-Prem Device 2 ─── Tunnel 3 ──→ Azure GW Instance 0 +On-Prem Device 2 ─── Tunnel 4 ──→ Azure GW Instance 1 +``` + +This creates a full-mesh with four tunnels and maximum redundancy. + +## Prerequisites + +- **Route-based VPN gateway** (policy-based does not support active-active) +- **Two public IP addresses** (Standard SKU, static allocation) +- **BGP is mandatory** for active-active configurations +- Minimum SKU: **VpnGw1** or higher (Basic does not support active-active) +- On-premises VPN device must support BGP and multiple tunnels + +## Deployment + +### Create Active-Active Gateway + +```bash +# Create two public IPs +az network public-ip create \ + --name --resource-group \ + --allocation-method Static --sku Standard --zone 1 2 3 + +az network public-ip create \ + --name --resource-group \ + --allocation-method Static --sku Standard --zone 1 2 3 + +# Create active-active VPN gateway with BGP +az network vnet-gateway create \ + --name \ + --resource-group \ + --vnet \ + --gateway-type Vpn \ + --vpn-type RouteBased \ + --sku VpnGw2AZ \ + --generation Generation2 \ + --public-ip-addresses \ + --asn 65515 \ + --no-wait +``` + +Note: Specifying two public IP addresses automatically enables active-active mode. + +### Create Local Network Gateways (One Per On-Prem Tunnel Endpoint) + +If the on-premises device has a single public IP, you still create a single local network gateway but establish two tunnels (one to each Azure gateway instance). + +```bash +az network local-gateway create \ + --name \ + --resource-group \ + --gateway-ip-address \ + --local-address-prefixes 10.0.0.0/8 \ + --bgp-peering-address \ + --asn +``` + +### Create Two VPN Connections (One Per Gateway Instance) + +```bash +# Connection to gateway instance 0 +az network vpn-connection create \ + --name \ + --resource-group \ + --vnet-gateway1 \ + --local-gateway2 \ + --shared-key \ + --enable-bgp true + +# Connection to gateway instance 1 +az network vpn-connection create \ + --name \ + --resource-group \ + --vnet-gateway1 \ + --local-gateway2 \ + --shared-key \ + --enable-bgp true +``` + +Both connections target the same local network gateway but the gateway automatically distributes them across the two instances. + +## BGP Configuration for Active-Active + +### How BGP Works with Active-Active + +Each gateway instance gets its own BGP peer IP from the GatewaySubnet: +- Instance 0: BGP IP = first usable IP from GatewaySubnet +- Instance 1: BGP IP = second usable IP from GatewaySubnet + +The on-prem device must establish BGP sessions with **both** instance IPs. + +### Retrieve Azure BGP Peer IPs + +```bash +az network vnet-gateway show \ + --name \ + --resource-group \ + --query "bgpSettings.bgpPeeringAddresses[].{ip:defaultBgpIpAddresses, tunnelIp:tunnelIpAddresses}" +``` + +### On-Premises Device Configuration + +The on-prem VPN device must: +1. Establish two IPsec tunnels — one to each Azure public IP +2. Configure BGP neighbor sessions to both Azure BGP peer IPs over the tunnels +3. Advertise the same on-prem prefixes to both BGP neighbors +4. Enable ECMP (Equal-Cost Multi-Path) to load-balance across both tunnels + +### ECMP and Load Balancing + +With active-active and BGP: +- Both tunnels advertise the same routes with equal metrics +- Azure uses ECMP to distribute traffic flows across both tunnels +- On-prem should also enable ECMP for balanced return traffic +- Individual TCP flows stick to one tunnel (per-flow hashing), but aggregate traffic is balanced + +## Failover Behavior + +| Scenario | Impact | Recovery | +|----------|--------|----------| +| Azure planned maintenance on one instance | Other instance handles all traffic. Brief disruption for flows on affected instance. | Automatic — flows re-establish on active instance. | +| On-prem tunnel to one instance fails | BGP detects failure, routes converge to remaining tunnel. | Automatic — BGP convergence in seconds to minutes. | +| Both tunnels fail (complete outage) | All connectivity lost. | Manual investigation required. | +| Gateway reset | Both instances restart. Full outage. | Gateway comes back online in 5-10 minutes. | + +### Failover Timing + +- **BGP hold timer:** Default 90 seconds. Routes withdrawn after 90s of no keepalive. +- **BFD (Bidirectional Forwarding Detection):** If available on the on-prem device, BFD reduces failover detection to sub-second times. +- **DPD (Dead Peer Detection):** Azure sends DPD probes every 45 seconds. Tunnel marked down after missed probes. + +## Active-Active VNet-to-VNet + +Active-active also applies to VNet-to-VNet connections between two Azure VPN gateways: + +```bash +# Gateway in VNet A (already active-active) +# Gateway in VNet B (also active-active) + +# Create connections in both directions +az network vpn-connection create \ + --name vnetA-to-vnetB \ + --resource-group \ + --vnet-gateway1 \ + --vnet-gateway2 \ + --shared-key \ + --enable-bgp true + +az network vpn-connection create \ + --name vnetB-to-vnetA \ + --resource-group \ + --vnet-gateway1 \ + --vnet-gateway2 \ + --shared-key \ + --enable-bgp true +``` + +This creates four tunnels (2 from each side) for maximum redundancy. + +## Converting Existing Gateway to Active-Active + +```bash +# Requires a second public IP +az network public-ip create \ + --name --resource-group \ + --allocation-method Static --sku Standard + +# Update gateway to active-active +az network vnet-gateway update \ + --name \ + --resource-group \ + --active-active true \ + --public-ip-addresses +``` + +**Warning:** Converting to active-active causes a brief gateway restart. Plan for maintenance window. + +## Monitoring Active-Active Gateways + +```bash +# Check BGP peer status (both instances should show peers) +az network vnet-gateway list-bgp-peer-status \ + --name \ + --resource-group + +# Check tunnel status for each connection +az network vpn-connection show \ + --name \ + --resource-group \ + --query connectionStatus + +az network vpn-connection show \ + --name \ + --resource-group \ + --query connectionStatus +``` + +### Azure Monitor Metrics + +- **TunnelAverageBandwidth** — per-tunnel bandwidth utilization +- **TunnelEgressBytes / TunnelIngressBytes** — data transferred per tunnel +- **BGPPeerStatus** — BGP session state per peer +- **TunnelEgressPacketDropCount** — packet drops indicating issues + +Set up alerts for: +- Tunnel disconnect events +- BGP peer state changes +- Bandwidth threshold breaches + +## Additional References + +- [Active-active VPN gateways](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-highlyavailable) +- [Configure active-active](https://learn.microsoft.com/azure/vpn-gateway/active-active-portal) +- [BGP with VPN Gateway](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-bgp-overview) diff --git a/plugin/skills/azure-vpn-gateway/references/ipsec-ike-params.md b/plugin/skills/azure-vpn-gateway/references/ipsec-ike-params.md new file mode 100644 index 000000000..dd3e78fbf --- /dev/null +++ b/plugin/skills/azure-vpn-gateway/references/ipsec-ike-params.md @@ -0,0 +1,173 @@ +# IPsec/IKE Parameters + +## Overview + +Azure VPN Gateway supports both default IPsec/IKE parameters and custom policies. Understanding these parameters is essential for interoperability with third-party VPN devices and compliance requirements. + +## Default IPsec/IKE Parameters + +When no custom policy is specified, Azure VPN Gateway negotiates using a set of default proposals. The gateway tries the proposals in order and selects the first match with the peer device. + +### IKE Phase 1 (Main Mode) Defaults + +| Parameter | Default Values | +|-----------|---------------| +| IKE Version | IKEv2 (IKEv1 for policy-based only) | +| Encryption | AES-256, AES-192, AES-128 | +| Integrity/PRF | SHA-384, SHA-256, SHA-1 | +| DH Group | DHGroup24, ECP384, ECP256, DHGroup14, DHGroup2 | +| SA Lifetime | 28,800 seconds (8 hours) | + +### IKE Phase 2 (Quick Mode / IPsec) Defaults + +| Parameter | Default Values | +|-----------|---------------| +| Encryption | AES-256-GCM, AES-128-GCM, AES-256-CBC, AES-192-CBC, AES-128-CBC | +| Integrity | SHA-256, SHA-1 (not used with GCM) | +| PFS Group | PFS24, ECP384, ECP256, PFS2, PFS1, None | +| SA Lifetime | 27,000 seconds (7.5 hours) | +| SA Size | 102,400,000 KB | + +## Custom IPsec/IKE Policy + +Custom policies let you specify exact algorithms for compliance or interoperability. When you set a custom policy, the gateway offers **only** the algorithms you specify. + +### Supported Algorithms + +#### IKE Phase 1 Encryption +- AES-256, AES-192, AES-128, DES3 (legacy, avoid) + +#### IKE Phase 1 Integrity +- SHA-384, SHA-256, SHA-1, MD5 (legacy, avoid) + +#### IKE Phase 1 DH Groups +- DHGroup24 (2048-bit MODP), DHGroup14 (2048-bit MODP), DHGroup2 (1024-bit, legacy) +- ECP384 (384-bit elliptic curve), ECP256 (256-bit elliptic curve) +- DHGroup2048, DHGroup1 (avoid) + +#### IPsec Phase 2 Encryption +- AES-256-GCM (recommended), AES-128-GCM (recommended) +- AES-256-CBC, AES-192-CBC, AES-128-CBC +- DES3 (legacy, avoid), DES (legacy, avoid), None (null encryption) + +#### IPsec Phase 2 Integrity +- SHA-256 (recommended), SHA-1 +- GCMAES-256, GCMAES-128 (used when GCM encryption is selected) +- MD5 (legacy, avoid) + +#### PFS Groups +- PFS24, ECP384, ECP256, PFS2048, PFS2, PFS1, None + +### Recommended Secure Configuration + +For new deployments, use this configuration as a baseline: + +``` +IKE Phase 1: AES-256 + SHA-256 + DHGroup14 (or ECP256) +IPsec Phase 2: AES-256-GCM + GCMAES-256 + PFS2048 (or ECP256) +SA Lifetime: 28800 seconds (Phase 1), 27000 seconds (Phase 2) +``` + +### CLI: Create Connection with Custom IPsec/IKE Policy + +```bash +# Create S2S connection with custom IPsec/IKE policy +az network vpn-connection create \ + --name \ + --resource-group \ + --vnet-gateway1 \ + --local-gateway2 \ + --shared-key + +# Apply custom IPsec/IKE policy to existing connection +az network vpn-connection ipsec-policy add \ + --connection-name \ + --resource-group \ + --ike-encryption AES256 \ + --ike-integrity SHA256 \ + --dh-group DHGroup14 \ + --ipsec-encryption GCMAES256 \ + --ipsec-integrity GCMAES256 \ + --pfs-group ECP256 \ + --sa-lifetime 27000 \ + --sa-max-size 102400000 + +# List IPsec policies on a connection +az network vpn-connection ipsec-policy list \ + --connection-name \ + --resource-group + +# Clear all custom policies (revert to defaults) +az network vpn-connection ipsec-policy clear \ + --connection-name \ + --resource-group +``` + +## UsePolicyBasedTrafficSelectors + +For connecting to policy-based on-prem devices from a route-based gateway, enable this flag: + +```bash +az network vpn-connection update \ + --name \ + --resource-group \ + --use-policy-based-traffic-selectors true +``` + +This creates policy-based traffic selectors for each on-prem prefix combination while keeping the route-based gateway. Useful for connecting to legacy Cisco ASA, older Palo Alto, or other policy-based devices. + +## DPD (Dead Peer Detection) + +- Azure VPN Gateway sends DPD keepalives every **45 seconds** by default +- If no response after **several retries**, the tunnel is marked as disconnected +- On-prem devices should be configured with compatible DPD timers +- Very aggressive DPD timers (<10s) may cause flapping on high-latency links + +## Connection Mode Settings + +Azure VPN Gateway supports: +- **Default** — gateway can be either initiator or responder +- **InitiatorOnly** — gateway always initiates the IKE connection +- **ResponderOnly** — gateway never initiates, only responds + +```bash +az network vpn-connection update \ + --name \ + --resource-group \ + --connection-mode InitiatorOnly +``` + +## Troubleshooting IKE/IPsec Failures + +### Common Issues + +| Symptom | Likely Cause | Resolution | +|---------|-------------|------------| +| Tunnel stays Connecting | IKE Phase 1 mismatch | Verify encryption, integrity, and DH group match on both sides | +| Tunnel connects then drops | Phase 2 rekey failure | Check IPsec SA lifetime and PFS settings match | +| Tunnel up but no traffic | Traffic selectors wrong | Verify local/remote address prefixes on both sides | +| Intermittent disconnects | DPD timeout mismatch | Align DPD timers; check internet stability | +| Slow throughput | Small packets or CBC mode | Use GCM algorithms; test with larger packets | + +### Diagnostic Commands + +```bash +# Check connection status +az network vpn-connection show \ + --name \ + --resource-group \ + --query "{status:connectionStatus, inBytes:ingressBytesTransferred, outBytes:egressBytesTransferred}" + +# Use Network Watcher VPN troubleshoot +az network watcher troubleshooting start \ + --resource \ + --resource-type vpnGateway \ + --storage-account \ + --storage-path +``` + +## Additional References + +- [About IPsec/IKE policy](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-ipsecikepolicy-rm-powershell) +- [Cryptographic requirements](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-about-compliance-crypto) +- [VPN device configuration samples](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-about-vpn-devices#devicetable) diff --git a/plugin/skills/azure-vpn-gateway/references/p2s-config.md b/plugin/skills/azure-vpn-gateway/references/p2s-config.md new file mode 100644 index 000000000..82cb0ec64 --- /dev/null +++ b/plugin/skills/azure-vpn-gateway/references/p2s-config.md @@ -0,0 +1,205 @@ +# Point-to-Site VPN Configuration + +## Overview + +Point-to-site (P2S) VPN connects individual client devices (laptops, tablets, phones) to an Azure VNet over an encrypted tunnel. Unlike S2S VPN, P2S does not require an on-premises VPN device or a public IP — it works from any internet connection. + +## P2S Tunnel Protocols + +| Protocol | Platforms | Port | Notes | +|----------|-----------|------|-------| +| **OpenVPN** | Windows, macOS, Linux, iOS, Android | TCP 443 or UDP 1194 | Recommended. Most flexible, widest platform support. | +| **IKEv2** | Windows, macOS | UDP 500, 4500 | Native OS support. Best for macOS. | +| **SSTP** | Windows only | TCP 443 | Firewall-friendly but limited to Windows. | + +### Protocol Selection Guidance + +- **Multi-platform:** Use OpenVPN. Works everywhere and can traverse most firewalls via TCP 443. +- **macOS clients:** Use IKEv2 for native integration, or OpenVPN for cross-platform consistency. +- **Windows-only, strict firewall:** SSTP works over TCP 443, passing through most corporate firewalls. However, OpenVPN on TCP 443 is equally firewall-friendly and supports more platforms. +- **You can enable multiple protocols** on the same gateway. The client chooses at connection time. + +## Authentication Methods + +### 1. Azure Certificate Authentication + +Client presents a certificate signed by a trusted root CA to authenticate. + +**Setup Steps:** + +```bash +# Configure P2S with certificate auth +az network vnet-gateway update \ + --name \ + --resource-group \ + --address-prefixes 172.16.0.0/24 \ + --client-protocol OpenVPN IkeV2 \ + --root-cert-name \ + --root-cert-data +``` + +**Certificate workflow:** +1. Generate a self-signed root certificate (or use enterprise PKI root) +2. Export root cert public key as Base64 and upload to Azure +3. Generate client certificates signed by the root cert +4. Install client cert on each client device +5. Download and install VPN client configuration + +**Revoke a client certificate:** + +```bash +az network vnet-gateway revoked-cert create \ + --name \ + --resource-group \ + --gateway-name \ + --thumbprint +``` + +### 2. Microsoft Entra ID (Azure AD) Authentication + +Users authenticate with their Entra ID credentials. Supports MFA and Conditional Access. + +**Requirements:** +- OpenVPN protocol only +- Azure VPN Client application (Windows, macOS) or OpenVPN client +- Entra ID tenant with VPN application registration + +**Setup Steps:** +1. Register the Azure VPN enterprise application in your Entra ID tenant +2. Grant admin consent for the VPN application +3. Configure the VPN gateway with Entra ID settings: + +```bash +az network vnet-gateway update \ + --name \ + --resource-group \ + --address-prefixes 172.16.0.0/24 \ + --client-protocol OpenVPN \ + --aad-tenant "https://login.microsoftonline.com/" \ + --aad-audience "" \ + --aad-issuer "https://sts.windows.net//" +``` + +**Benefits:** +- Users sign in with corporate credentials +- Supports MFA enforcement +- Conditional Access policies apply (device compliance, location, risk) +- No certificate distribution needed + +### 3. RADIUS Authentication + +Delegates authentication to an existing RADIUS server (NPS, FreeRADIUS, etc.). + +**Setup Steps:** + +```bash +az network vnet-gateway update \ + --name \ + --resource-group \ + --address-prefixes 172.16.0.0/24 \ + --client-protocol OpenVPN IkeV2 \ + --radius-server \ + --radius-secret +``` + +**Use cases:** +- Integration with existing enterprise identity (AD, LDAP) via RADIUS +- OTP/token-based MFA through RADIUS +- Multiple RADIUS servers for redundancy + +### Authentication Selection Guidance + +| Requirement | Recommended Auth | +|------------|-----------------| +| Entra ID users, MFA, Conditional Access | Entra ID auth | +| Existing PKI infrastructure | Certificate auth | +| Existing RADIUS/NPS infrastructure | RADIUS auth | +| Non-Windows clients without Entra ID | Certificate auth | +| Maximum security with zero trust | Entra ID auth + Conditional Access | + +## Client Address Pool + +The P2S address pool provides IP addresses to connecting clients. Choose a range that does not overlap with: +- VNet address space +- On-premises address ranges +- Other connected VNet ranges + +Common choices: `172.16.0.0/24`, `192.168.100.0/24` + +For large user bases, use a larger pool: `172.16.0.0/16` (supports 65,534 concurrent connections, subject to SKU limits). + +## Download VPN Client Configuration + +```bash +# Generate VPN client configuration package +az network vnet-gateway vpn-client generate \ + --name \ + --resource-group \ + --processor-architecture Amd64 + +# This returns a URL to download a ZIP file containing: +# - OpenVPN profile (*.ovpn) +# - IKEv2 configuration files +# - SSTP configuration (Windows) +``` + +For Entra ID auth, clients use the **Azure VPN Client** app (available from Microsoft Store or direct download). + +## Custom DNS for P2S Clients + +To configure custom DNS servers pushed to P2S clients: + +```bash +az network vnet-gateway update \ + --name \ + --resource-group \ + --custom-routes 10.0.0.0/8 \ + --vpn-client-root-certificates +``` + +For DNS resolution of Azure private endpoints from P2S clients, point DNS to: +- Azure DNS Private Resolver inbound endpoint in the VNet +- Or a custom DNS server in the VNet that forwards to Azure DNS (168.63.129.16) + +## P2S Connection Limits by SKU + +| SKU | Max P2S Connections | +|-----|---------------------| +| Basic | 128 | +| VpnGw1/1AZ | 250 | +| VpnGw2/2AZ | 500 | +| VpnGw3/3AZ | 1,000 | +| VpnGw4/4AZ | 5,000 | +| VpnGw5/5AZ | 10,000 | + +## Troubleshooting P2S + +### Client Cannot Connect + +1. **Check protocol** — ensure client uses a protocol enabled on the gateway +2. **Certificate issues** — verify client cert is not expired and is signed by a root cert uploaded to Azure +3. **Entra ID issues** — verify tenant ID, audience, and issuer are correct; check admin consent was granted +4. **Firewall blocking** — ensure UDP 500/4500 (IKEv2), TCP 443 (SSTP/OpenVPN), or UDP 1194 (OpenVPN) are open +5. **Client address pool exhausted** — enlarge the pool if near the SKU's concurrent connection limit + +### Client Connects but Cannot Reach Resources + +1. **Routing** — VNet resources must have routes back to the P2S client address pool (automatic with VPN gateway) +2. **NSG rules** — Azure VMs must allow traffic from the P2S client address pool +3. **DNS resolution** — P2S clients may not resolve Azure private DNS zones unless custom DNS is configured +4. **Split tunneling** — by default, P2S uses forced tunneling. If split tunneling is needed, configure custom routes + +### Check Connected P2S Clients + +```bash +az network vnet-gateway list-bgp-peer-status \ + --name \ + --resource-group +``` + +## Additional References + +- [About P2S VPN](https://learn.microsoft.com/azure/vpn-gateway/point-to-site-about) +- [Configure P2S with certificate auth](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-howto-point-to-site-resource-manager-portal) +- [Configure P2S with Entra ID auth](https://learn.microsoft.com/azure/vpn-gateway/point-to-site-entra-gateway) +- [Azure VPN Client](https://learn.microsoft.com/azure/vpn-gateway/point-to-site-entra-vpn-client-windows) diff --git a/plugin/skills/azure-vpn-gateway/references/s2s-config.md b/plugin/skills/azure-vpn-gateway/references/s2s-config.md new file mode 100644 index 000000000..19999be77 --- /dev/null +++ b/plugin/skills/azure-vpn-gateway/references/s2s-config.md @@ -0,0 +1,242 @@ +# Site-to-Site VPN Configuration + +## S2S VPN Setup Checklist + +Use this checklist for every site-to-site VPN deployment. Complete each step in order. + +### Prerequisites + +- [ ] Azure VNet created with a non-overlapping address space relative to on-premises +- [ ] GatewaySubnet created (/27 or larger recommended) +- [ ] Public IP address allocated for the VPN gateway +- [ ] On-premises VPN device public IP address known and reachable +- [ ] On-premises network address prefixes documented +- [ ] Shared key (PSK) agreed upon and securely stored +- [ ] On-premises VPN device validated against [Azure compatibility list](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-about-vpn-devices) + +### Step 1: Create the VPN Gateway + +```bash +# Create public IP for gateway +az network public-ip create \ + --name \ + --resource-group \ + --allocation-method Static \ + --sku Standard \ + --zone 1 2 3 + +# Create VPN gateway (takes 30-45 minutes) +az network vnet-gateway create \ + --name \ + --resource-group \ + --vnet \ + --gateway-type Vpn \ + --vpn-type RouteBased \ + --sku VpnGw2AZ \ + --generation Generation2 \ + --public-ip-addresses \ + --no-wait + +# Check provisioning status +az network vnet-gateway show \ + --name \ + --resource-group \ + --query provisioningState +``` + +### Step 2: Create the Local Network Gateway + +The local network gateway represents your on-premises VPN device in Azure. + +```bash +az network local-gateway create \ + --name \ + --resource-group \ + --gateway-ip-address \ + --local-address-prefixes 10.1.0.0/16 10.2.0.0/16 +``` + +For BGP-enabled connections, add BGP settings: + +```bash +az network local-gateway create \ + --name \ + --resource-group \ + --gateway-ip-address \ + --local-address-prefixes 10.1.0.0/16 \ + --bgp-peering-address \ + --asn +``` + +### Step 3: Create the VPN Connection + +```bash +az network vpn-connection create \ + --name \ + --resource-group \ + --vnet-gateway1 \ + --local-gateway2 \ + --shared-key \ + --connection-protocol IKEv2 +``` + +### Step 4: Configure the On-Premises Device + +Configure the on-premises VPN device with: +- Azure gateway public IP (obtained from `az network public-ip show`) +- Shared key (same PSK as step 3) +- IKE/IPsec parameters matching Azure defaults (or custom policy) +- Traffic selectors for Azure VNet address space + +### Step 5: Verify Connectivity + +```bash +# Check connection status (should show "Connected") +az network vpn-connection show \ + --name \ + --resource-group \ + --query connectionStatus + +# Check data transfer +az network vpn-connection show \ + --name \ + --resource-group \ + --query "{status:connectionStatus, inBytes:ingressBytesTransferred, outBytes:egressBytesTransferred}" +``` + +## Optional: Enable BGP + +BGP provides dynamic route exchange, eliminating the need to maintain static routes when address spaces change. + +```bash +# Enable BGP on the VPN gateway (at creation time) +az network vnet-gateway create \ + --name \ + --resource-group \ + --vnet \ + --gateway-type Vpn \ + --vpn-type RouteBased \ + --sku VpnGw2AZ \ + --public-ip-addresses \ + --asn 65515 + +# Enable BGP on the connection +az network vpn-connection update \ + --name \ + --resource-group \ + --enable-bgp true + +# Verify BGP peers +az network vnet-gateway list-bgp-peer-status \ + --name \ + --resource-group + +# View learned BGP routes +az network vnet-gateway list-learned-routes \ + --name \ + --resource-group + +# View advertised BGP routes to a peer +az network vnet-gateway list-advertised-routes \ + --name \ + --resource-group \ + --peer +``` + +### BGP Configuration Notes + +- Azure VPN Gateway default ASN: **65515** +- The BGP peer IP on the Azure side is automatically assigned from the GatewaySubnet range +- On-premises ASN must be different from Azure ASN (do not use 65515) +- Reserved ASNs to avoid: 0, 23456, 64496-64511, 65535, 4294967295 +- Azure reserved: 65515 (default), 65520 (multi-site) + +## Multi-Site S2S VPN + +A single route-based VPN gateway can connect to multiple on-premises sites. + +```bash +# Site 1 +az network local-gateway create --name site1-lgw --resource-group \ + --gateway-ip-address --local-address-prefixes 10.1.0.0/16 +az network vpn-connection create --name site1-conn --resource-group \ + --vnet-gateway1 --local-gateway2 site1-lgw --shared-key + +# Site 2 +az network local-gateway create --name site2-lgw --resource-group \ + --gateway-ip-address --local-address-prefixes 10.2.0.0/16 +az network vpn-connection create --name site2-conn --resource-group \ + --vnet-gateway1 --local-gateway2 site2-lgw --shared-key +``` + +Each site counts toward the S2S tunnel limit for the gateway SKU. + +## NAT Rules for Overlapping Address Spaces + +When on-premises networks have overlapping IP ranges, use VPN Gateway NAT rules: + +```bash +# Create ingress SNAT rule (translate on-prem source to new range) +az network vnet-gateway nat-rule add \ + --name \ + --resource-group \ + --gateway-name \ + --internal-mappings 10.1.0.0/24 \ + --external-mappings 172.16.1.0/24 \ + --type Static \ + --mode IngressSnat + +# Associate NAT rule with a connection +az network vpn-connection update \ + --name \ + --resource-group \ + --ingress-nat-rule +``` + +## Troubleshooting S2S Connectivity + +### Connection Shows "Connecting" (Not Connected) + +1. **Verify on-prem device config** — public IP, PSK, and IKE parameters must match +2. **Check IKE version** — ensure both sides use IKEv2 +3. **Verify firewall rules** — allow UDP 500 (IKE), UDP 4500 (NAT-T), and ESP (protocol 50) to the on-prem public IP +4. **Check for NAT** — if the on-prem device is behind NAT, ensure NAT-T is enabled +5. **Reset the gateway** — `az network vnet-gateway reset --name --resource-group ` + +### Connection Shows "Connected" but No Traffic + +1. **Check routing** — verify local network gateway prefixes include all on-prem ranges +2. **Check NSGs** — NSG rules on Azure VMs must allow traffic from on-prem source IPs +3. **Check on-prem routing** — on-prem router must have routes pointing to the VPN tunnel for Azure address space +4. **Check UDRs** — user-defined routes must not black-hole VPN traffic +5. **Ping test** — test with ICMP (ensure ICMP is allowed by both NSGs and on-prem firewall) + +### Intermittent Connectivity + +1. **DPD timers** — mismatched Dead Peer Detection settings cause tunnel flapping +2. **SA lifetime mismatch** — both sides should agree on IKE and IPsec SA lifetimes +3. **MTU issues** — VPN overhead reduces effective MTU to ~1400 bytes; enable PMTUD or clamp MSS +4. **ISP stability** — check internet connectivity stability independent of the VPN + +### Diagnostic Tools + +```bash +# Network Watcher VPN diagnostics +az network watcher troubleshooting start \ + --resource \ + --resource-type vpnGateway \ + --storage-account \ + --storage-path + +# Check gateway health +az network vnet-gateway show \ + --name \ + --resource-group \ + --query "{state:provisioningState, bgp:enableBgp, activeActive:activeActive}" +``` + +## Additional References + +- [Create S2S VPN connection](https://learn.microsoft.com/azure/vpn-gateway/tutorial-site-to-site-portal) +- [Validated VPN devices](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-about-vpn-devices) +- [Troubleshoot S2S VPN](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-troubleshoot-site-to-site-cannot-connect) diff --git a/plugin/skills/azure-vpn-gateway/references/vpn-types.md b/plugin/skills/azure-vpn-gateway/references/vpn-types.md new file mode 100644 index 000000000..e5dcf758a --- /dev/null +++ b/plugin/skills/azure-vpn-gateway/references/vpn-types.md @@ -0,0 +1,97 @@ +# VPN Types and Gateway SKUs + +## Policy-Based vs Route-Based VPN + +Azure VPN Gateway supports two VPN types that determine how traffic selectors are constructed and how tunnels are established. + +### Policy-Based VPN (IKEv1) + +- Uses **traffic selectors** (source/destination IP prefix pairs) to determine which traffic enters the tunnel +- Supports **one S2S tunnel only** per gateway +- Does **not** support point-to-site (P2S) connections +- Does **not** support VNet-to-VNet connections +- Does **not** support coexistence with ExpressRoute +- Does **not** support BGP +- Limited to IKEv1 +- Available only on **Basic** SKU +- Use case: legacy on-premises devices that require policy-based IPsec + +### Route-Based VPN (IKEv2) + +- Uses an **any-to-any** (wildcard) traffic selector and routing tables to direct traffic +- Supports **multiple S2S tunnels** (count depends on SKU) +- Supports **P2S connections** (OpenVPN, IKEv2, SSTP) +- Supports **VNet-to-VNet** connections +- Supports **coexistence with ExpressRoute** gateway on the same VNet +- Supports **BGP** dynamic routing +- Supports **active-active** configuration +- Supports **custom IPsec/IKE policies** +- Recommended for virtually all scenarios + +## Gateway SKU Comparison + +| SKU | Max S2S Tunnels | Max P2S Connections | Aggregate Throughput Benchmark | BGP | AZ Support | +|-----|-----------------|---------------------|-------------------------------|-----|------------| +| Basic | 10 | 128 | 100 Mbps | No | No | +| VpnGw1 | 30 | 250 | 650 Mbps | Yes | No | +| VpnGw2 | 30 | 500 | 1 Gbps | Yes | No | +| VpnGw3 | 30 | 1,000 | 1.25 Gbps | Yes | No | +| VpnGw4 | 100 | 5,000 | 5 Gbps | Yes | No | +| VpnGw5 | 100 | 10,000 | 10 Gbps | Yes | No | +| VpnGw1AZ | 30 | 250 | 650 Mbps | Yes | Yes | +| VpnGw2AZ | 30 | 500 | 1 Gbps | Yes | Yes | +| VpnGw3AZ | 30 | 1,000 | 1.25 Gbps | Yes | Yes | +| VpnGw4AZ | 100 | 5,000 | 5 Gbps | Yes | Yes | +| VpnGw5AZ | 100 | 10,000 | 10 Gbps | Yes | Yes | + +### SKU Selection Guidance + +1. **Avoid Basic SKU** for production. It lacks BGP, zone-redundancy, and active-active support. It is a legacy SKU. +2. **Use AZ SKUs in production.** VpnGw1AZ through VpnGw5AZ provide zone-redundant deployments. If the region supports availability zones, always use AZ variants. +3. **Start with VpnGw2AZ** for most production workloads. It provides 1 Gbps throughput, supports up to 30 tunnels, and allows in-place upgrade to VpnGw3AZ/4AZ/5AZ without redeployment. +4. **VpnGw4AZ or VpnGw5AZ** for large enterprises with 30+ branches or high aggregate throughput requirements. +5. **Throughput benchmarks are aggregates**, not per-tunnel guarantees. A VpnGw2 (1 Gbps) gateway with 10 tunnels shares the 1 Gbps across all tunnels. + +### In-Place SKU Resize + +You can resize within a generation without redeployment: +- VpnGw1 ↔ VpnGw2 ↔ VpnGw3 ↔ VpnGw4 ↔ VpnGw5 (same for AZ variants) +- You **cannot** resize from Basic to VpnGw SKUs (requires redeployment) +- You **cannot** resize from non-AZ to AZ variants (requires redeployment) + +```bash +# Resize a VPN gateway SKU (no downtime for resize within generation) +az network vnet-gateway update \ + --name \ + --resource-group \ + --sku VpnGw3AZ +``` + +## Throughput Benchmarks and Real-World Expectations + +Azure publishes aggregate throughput benchmarks per SKU. Actual throughput depends on: + +- **Number of tunnels** sharing the gateway +- **Packet size** — small packets (64B) significantly reduce throughput vs large packets (1400B) +- **Encryption algorithm** — AES-256-GCM is faster than AES-256-CBC on the gateway hardware +- **Latency to on-prem** — higher RTT reduces TCP throughput over the tunnel +- **Single tunnel limit** — a single IPsec tunnel typically maxes out at approximately 1-1.25 Gbps regardless of SKU + +### Practical guidance + +- If you need more than 1 Gbps to a single site, configure **active-active** with BGP to establish 2-4 tunnels and load-balance using ECMP +- For throughput testing, use tools like `iperf3` with multiple parallel streams and large window sizes +- Monitor gateway metrics: `TunnelBandwidth`, `TunnelEgressBytes`, `TunnelIngressBytes` in Azure Monitor + +## VPN Gateway Deployment Time + +- Gateway creation typically takes **30-45 minutes** +- SKU resizing takes **approximately 30 minutes** +- Gateway reset takes **5-10 minutes** (resets active connections) +- Plan change windows accordingly + +## Additional References + +- [About VPN Gateway SKUs](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-about-vpn-gateway-settings#gwsku) +- [VPN Gateway FAQ](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-vpn-faq) +- [Validated VPN devices](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-about-vpn-devices) diff --git a/plugin/skills/azure-waf/SKILL.md b/plugin/skills/azure-waf/SKILL.md new file mode 100644 index 000000000..ce4922c38 --- /dev/null +++ b/plugin/skills/azure-waf/SKILL.md @@ -0,0 +1,157 @@ +--- +name: azure-waf +description: "Configure and manage Web Application Firewall (WAF) on Application Gateway and Azure Front Door to protect web applications from OWASP threats, SQL injection, XSS, and bot attacks. WHEN: WAF, web application firewall, OWASP, SQL injection, XSS, cross-site scripting, bot protection, WAF policy, managed rules, custom WAF rules, WAF exclusions. DO NOT USE FOR: network-level firewall (use azure-firewall), DDoS mitigation (use azure-ddos-protection), NSG rules (use azure-virtual-network)." +license: MIT +metadata: + author: Microsoft + version: "0.0.0-placeholder" +--- + +# Azure Web Application Firewall (WAF) + +Azure Web Application Firewall provides centralized protection for web applications against common exploits and vulnerabilities. WAF can be deployed on Azure Application Gateway (regional) or Azure Front Door (global edge). It inspects every inbound HTTP/HTTPS request and applies managed rule sets (OWASP CRS, Microsoft DRS) and custom rules to detect and block attacks like SQL injection, cross-site scripting, and bot abuse. + +## When to Use This Skill + +- Deploying or configuring WAF on Application Gateway v2 or Azure Front Door +- Selecting between Detection mode and Prevention mode +- Configuring OWASP Core Rule Set (CRS) or Microsoft Default Rule Set (DRS) versions +- Creating custom WAF rules with match conditions and rate limiting +- Troubleshooting false positives and configuring WAF exclusions +- Setting up bot protection rules against automated threats +- Tuning WAF rules for specific application needs +- Migrating WAF configuration between Application Gateway and Front Door +- Reviewing WAF logs and diagnostics to identify blocked requests +- Configuring geo-filtering rules to restrict traffic by country or region + +## Rules + +1. WAF on Application Gateway and WAF on Front Door use different policy schemas — configurations are not interchangeable. Always confirm which platform the user is targeting. +2. Start with Detection mode in production to identify false positives before enabling Prevention mode. Review WAF logs for at least a few days of production traffic. +3. WAF policies are the recommended configuration model. Legacy WAF configuration on Application Gateway (via `waf-config`) is deprecated. +4. CRS is used on Application Gateway; DRS (Default Rule Set) is used on Front Door. Know which rule set applies to the target platform. +5. When disabling specific managed rules, prefer per-rule exclusions over globally disabling rule groups to maintain maximum protection. +6. Rate limiting rules on Front Door WAF use a different match condition structure than Application Gateway — verify the platform before providing syntax. +7. Always recommend enabling WAF diagnostic logs to a Log Analytics workspace for visibility into matched rules and blocked requests. +8. Bot protection managed rule set is available on both Application Gateway and Front Door, but must be explicitly added to the WAF policy. +9. Custom rules are evaluated before managed rules. Within custom rules, priority determines order (lowest number first). +10. Cross-reference with `azure-application-gateway` for Application Gateway-specific settings and with `azure-front-door` for Front Door routing; recommend `azure-ddos-protection` for volumetric attack mitigation that WAF does not address. + +## MCP Tools + +| Tool | Resource | Use | +|------|----------|-----| +| `azure__network` | `waf_policy_list` | List all WAF policies in a subscription or resource group | + +## CLI Fallback + +```bash +# List WAF policies for Application Gateway +az network application-gateway waf-policy list \ + --resource-group -o table + +# Create a WAF policy for Application Gateway +az network application-gateway waf-policy create \ + --name \ + --resource-group \ + --type OWASP \ + --version 3.2 + +# Enable Prevention mode on Application Gateway WAF policy +az network application-gateway waf-policy policy-setting update \ + --policy-name \ + --resource-group \ + --state Enabled \ + --mode Prevention + +# Add a custom rule to Application Gateway WAF policy +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "BlockBadIP" \ + --priority 10 \ + --action Block \ + --rule-type MatchRule + +# Add a match condition to the custom rule +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "BlockBadIP" \ + --match-variables RemoteAddr \ + --operator IPMatch \ + --values "203.0.113.0/24" + +# Create a managed rule override (disable a specific rule) +az network application-gateway waf-policy managed-rule rule-set update \ + --policy-name \ + --resource-group \ + --type OWASP \ + --version 3.2 \ + --group-name REQUEST-942-APPLICATION-ATTACK-SQLI \ + --rules 942130 \ + --state Disabled + +# Add exclusion to WAF policy +az network application-gateway waf-policy managed-rule exclusion add \ + --policy-name \ + --resource-group \ + --match-variable RequestHeaderNames \ + --selector-match-operator Contains \ + --selector "X-Custom-Header" + +# List Front Door WAF policies +az network front-door waf-policy list \ + --resource-group -o table + +# Create a Front Door WAF policy +az network front-door waf-policy create \ + --name \ + --resource-group \ + --mode Prevention \ + --sku Premium_AzureFrontDoor + +# Add a rate-limit custom rule to Front Door WAF +az network front-door waf-policy rule create \ + --policy-name \ + --resource-group \ + --name "RateLimitAPI" \ + --priority 100 \ + --action Block \ + --rule-type RateLimitRule \ + --rate-limit-threshold 100 \ + --rate-limit-duration 1 + +# Enable WAF diagnostic logging +az monitor diagnostic-settings create \ + --name "waf-diag" \ + --resource \ + --workspace \ + --logs '[{"categoryGroup":"allLogs","enabled":true}]' +``` + +## Key Concepts + +- **WAF platforms**: Application Gateway WAF (regional, inline with app) vs Front Door WAF (global edge, CDN-integrated); choose based on deployment model +- **Detection vs Prevention**: Detection mode logs but does not block; Prevention mode actively blocks matching requests; always start with Detection in production +- **Managed rule sets**: OWASP CRS (3.2, 3.1, 3.0) for Application Gateway; Microsoft DRS (2.1, 2.0, 1.1) for Front Door; both cover OWASP Top 10 +- **Custom rules**: User-defined rules with match conditions; evaluated before managed rules; support IP matching, geo-filtering, rate limiting, string matching +- **Rule evaluation order**: Custom rules (by priority) → Managed rules (by rule group and rule ID); first matching rule determines action +- **Exclusions**: Exclude specific request attributes (headers, cookies, query parameters, body fields) from specific managed rules to eliminate false positives +- **Bot protection**: Managed rule set that classifies bots as good (search engines) or bad (scrapers, crawlers); can allow, block, or log per category +- **Rate limiting**: Limits request rate per client IP (or per socket address on Front Door); uses sliding window; available via custom rules +- **Per-rule exclusions**: Scoped exclusions that apply to a specific managed rule rather than globally — more secure than disabling the rule entirely +- **Anomaly scoring**: CRS 3.2+ on Application Gateway uses anomaly scoring — a request must exceed a score threshold to be blocked, reducing false positives +- **WAF policy association**: One WAF policy can be associated with multiple Application Gateways or Front Door endpoints; changes propagate to all associations +- **Geo-filtering**: Block or allow traffic from specific countries or regions using custom rules with GeoMatch operator + +## References + +- [waf-modes.md](references/waf-modes.md) — Detection vs Prevention mode guidance +- [managed-rules.md](references/managed-rules.md) — OWASP CRS and Microsoft DRS rule sets +- [custom-rules.md](references/custom-rules.md) — Custom rule creation and rate limiting +- [exclusions.md](references/exclusions.md) — WAF exclusion configuration and false positive handling +- [bot-protection.md](references/bot-protection.md) — Bot Manager rule set configuration +- [Azure WAF on Application Gateway documentation](https://learn.microsoft.com/azure/web-application-firewall/ag/ag-overview) +- [Azure WAF on Front Door documentation](https://learn.microsoft.com/azure/web-application-firewall/afds/afds-overview) +- [WAF policy overview](https://learn.microsoft.com/azure/web-application-firewall/overview) diff --git a/plugin/skills/azure-waf/references/bot-protection.md b/plugin/skills/azure-waf/references/bot-protection.md new file mode 100644 index 000000000..c906c97d0 --- /dev/null +++ b/plugin/skills/azure-waf/references/bot-protection.md @@ -0,0 +1,249 @@ +# WAF Bot Protection + +Azure WAF Bot Manager rule set provides protection against automated bot traffic. It classifies bots into categories and lets you allow, block, or log based on bot type. Bot protection is available on both Application Gateway WAF and Front Door WAF. + +## Bot Manager Rule Set + +The Bot Manager rule set is a managed rule set that must be explicitly added to your WAF policy — it is not included in the default OWASP CRS or Microsoft DRS. + +### Bot categories + +| Category | Description | Default action | Examples | +|----------|-------------|---------------|----------| +| **GoodBot** | Known legitimate bots that should be allowed | Allow | Googlebot, Bingbot, Slurp (Yahoo), Facebookbot, LinkedInBot | +| **BadBot** | Known malicious or unwanted bots | Block | Known scrapers, spam bots, vulnerability scanners | +| **UnknownBot** | Bots that cannot be classified into good or bad | Log (configurable) | Custom crawlers, unrecognized automated tools | + +### How bot detection works + +Bot Manager uses multiple signals to classify traffic: +1. **User-Agent string analysis** — matches against a database of known bot signatures +2. **IP reputation** — cross-references source IPs against known bot infrastructure +3. **Behavioral patterns** — identifies bot-like request patterns (rapid-fire, systematic crawling) +4. **JavaScript challenge** (Front Door Premium) — client-side challenge that distinguishes browsers from headless bots + +## Enabling Bot Protection + +### Application Gateway WAF + +```bash +# Add Bot Manager rule set to an existing WAF policy +az network application-gateway waf-policy managed-rule rule-set add \ + --policy-name \ + --resource-group \ + --type Microsoft_BotManagerRuleSet \ + --version 1.0 +``` + +The latest version is 1.1 (if available in your region). Check available versions: + +```bash +az network application-gateway waf-policy managed-rule rule-set list \ + --query "[?ruleSetType=='Microsoft_BotManagerRuleSet']" -o table +``` + +### Front Door WAF + +Bot Manager on Front Door is configured through the managed rule sets in the WAF policy. Use the portal or ARM template: + +```json +{ + "managedRules": { + "managedRuleSets": [ + { + "ruleSetType": "Microsoft_DefaultRuleSet", + "ruleSetVersion": "2.1" + }, + { + "ruleSetType": "Microsoft_BotManagerRuleSet", + "ruleSetVersion": "1.0" + } + ] + } +} +``` + +## Customizing Bot Rules + +### Override a specific bot rule action + +You can change the action for individual bot rules. For example, to block unknown bots instead of just logging: + +```bash +# Application Gateway: change UnknownBot action from Log to Block +az network application-gateway waf-policy managed-rule rule-set update \ + --policy-name \ + --resource-group \ + --type Microsoft_BotManagerRuleSet \ + --version 1.0 \ + --group-name UnknownBots \ + --rules 300700 \ + --action Block +``` + +### Common bot rule IDs + +| Rule ID | Category | Description | +|---------|----------|-------------| +| 300100 | GoodBot | Known search engine crawlers | +| 300200 | GoodBot | Known legitimate service bots | +| 300300 | BadBot | Known malicious bot signatures | +| 300400 | BadBot | Known scraping tools | +| 300500 | BadBot | Known vulnerability scanners | +| 300600 | BadBot | Known spam bots | +| 300700 | UnknownBot | Unclassified bot traffic | + +> **Note**: Rule IDs and groupings may vary by version. Check the latest documentation for your rule set version. + +## Combining Bot Protection with Custom Rules + +For more granular bot control, combine the Bot Manager rule set with custom rules: + +### Allow specific bot IPs (bypass bot check) + +```bash +# Create an allow rule for your monitoring bot +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "AllowMonitoringBot" \ + --priority 1 \ + --action Allow \ + --rule-type MatchRule + +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "AllowMonitoringBot" \ + --match-variables RemoteAddr \ + --operator IPMatch \ + --values "10.0.5.10" "10.0.5.11" +``` + +Since custom rules evaluate before managed rules, this Allow rule bypasses bot detection for your monitoring infrastructure. + +### Block specific User-Agent patterns not covered by Bot Manager + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "BlockCustomScrapers" \ + --priority 5 \ + --action Block \ + --rule-type MatchRule + +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "BlockCustomScrapers" \ + --match-variables "RequestHeaders['User-Agent']" \ + --operator Contains \ + --values "my-custom-scraper" "data-harvester" \ + --transforms Lowercase +``` + +### Rate limit suspected bot traffic + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "RateLimitBots" \ + --priority 10 \ + --action Block \ + --rule-type RateLimitRule \ + --rate-limit-threshold 60 \ + --rate-limit-duration OneMin \ + --group-by-user-session "ClientAddr" + +# Scope to requests without common browser headers +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "RateLimitBots" \ + --match-variables "RequestHeaders['Accept-Language']" \ + --operator Equal \ + --negate true \ + --values "*" +``` + +## Platform Differences + +| Feature | Application Gateway WAF | Front Door WAF | +|---------|------------------------|----------------| +| Bot Manager versions | 1.0, 1.1 | 1.0, 1.1 | +| JavaScript challenge | Not available | Available (Premium tier) | +| Bot detection signals | UA string, IP reputation | UA string, IP reputation, behavioral, JS challenge | +| Custom bot rules | Via custom WAF rules | Via custom WAF rules | +| Bot traffic logging | WAF diagnostic logs | WAF diagnostic logs + Front Door access logs | + +### Front Door JavaScript Challenge + +Front Door Premium tier offers a JavaScript challenge capability: +- When a request is suspected to be from a bot, the client receives a JavaScript challenge page +- Real browsers execute the JavaScript and are allowed through +- Headless bots and scripts that cannot execute JavaScript are blocked +- This is particularly effective against sophisticated bots that spoof User-Agent strings + +## Monitoring Bot Traffic + +### Log Analytics query: Bot rule matches + +```kusto +AzureDiagnostics +| where Category == "ApplicationGatewayFirewallLog" +| where ruleGroup_s contains "Bot" +| summarize count() by ruleId_s, action_s, bin(TimeGenerated, 1h) +| render timechart +``` + +### Log Analytics query: Top blocked bot IPs + +```kusto +AzureDiagnostics +| where Category == "ApplicationGatewayFirewallLog" +| where ruleGroup_s contains "Bot" +| where action_s == "Blocked" +| summarize BlockCount = count() by clientIp_s +| order by BlockCount desc +| take 20 +``` + +### Log Analytics query: Good bot traffic volume + +```kusto +AzureDiagnostics +| where Category == "ApplicationGatewayFirewallLog" +| where ruleGroup_s == "GoodBots" +| summarize count() by ruleId_s, bin(TimeGenerated, 1d) +| render timechart +``` + +## Best Practices + +1. **Always add the Bot Manager rule set** — OWASP CRS and Microsoft DRS do not include bot detection; it must be added separately +2. **Start with default actions** — GoodBot: Allow, BadBot: Block, UnknownBot: Log; tune after reviewing logs +3. **Allow your own automation** — Use custom Allow rules (by IP) for your own monitoring, CI/CD, and health check bots +4. **Review UnknownBot logs regularly** — Reclassify frequently seen unknown bots as Allow or Block using rule overrides +5. **Use rate limiting as a complement** — Bot Manager catches known bots; rate limiting catches unknown bots hitting endpoints too fast +6. **Keep the rule set version updated** — Microsoft continuously updates bot signatures; upgrade to the latest version when available +7. **Combine with DDoS Protection** for comprehensive defense — bot attacks at scale become DDoS attacks; see `azure-ddos-protection` + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Search engine crawler blocked | GoodBot rule overridden to Block | Reset GoodBot rule action to Allow | +| Bad bot not detected | Bot not in signature database | Add a custom rule matching the bot's User-Agent or IP | +| Too many UnknownBot alerts | Default action is Log for unknown bots | Review and reclassify frequent unknown bots; consider blocking persistent offenders | +| JavaScript challenge not working | Using Application Gateway (not supported) or Standard Front Door | Upgrade to Front Door Premium tier | +| Own monitoring tools blocked | Not exempted | Add a custom Allow rule for monitoring IPs | + +## Related + +- [custom-rules.md](custom-rules.md) — Custom rules for advanced bot control +- [managed-rules.md](managed-rules.md) — Core managed rule sets (CRS/DRS) +- [waf-modes.md](waf-modes.md) — Detection vs Prevention for testing bot rules +- [Bot protection documentation (Application Gateway)](https://learn.microsoft.com/azure/web-application-firewall/ag/bot-protection-overview) +- [Bot protection documentation (Front Door)](https://learn.microsoft.com/azure/web-application-firewall/afds/waf-front-door-configure-bot-protection) diff --git a/plugin/skills/azure-waf/references/custom-rules.md b/plugin/skills/azure-waf/references/custom-rules.md new file mode 100644 index 000000000..89c5f8a6a --- /dev/null +++ b/plugin/skills/azure-waf/references/custom-rules.md @@ -0,0 +1,293 @@ +# WAF Custom Rules + +Custom rules let you define your own match conditions and actions to handle scenarios that managed rule sets do not cover. Custom rules are evaluated **before** managed rules and follow a priority-based order. + +## Custom Rule Evaluation Order + +``` +Incoming request + │ + ▼ +Custom Rules (by priority, lowest first) + │ Match → Execute action (Allow, Block, Log, Redirect) + │ No match → continue to next custom rule + ▼ +Managed Rules (OWASP CRS / Microsoft DRS) + │ + ▼ +Default action (allow if no rule matched) +``` + +If a custom rule with an **Allow** action matches, the request bypasses all subsequent custom rules and managed rules. + +## Custom Rule Structure + +| Field | Description | Required | +|-------|-------------|----------| +| Name | Unique rule name (alphanumeric, hyphens, underscores) | Yes | +| Priority | Processing order (1–100); lower = processed first | Yes | +| Rule type | `MatchRule` or `RateLimitRule` | Yes | +| Match conditions | One or more conditions that must ALL be true (AND logic) | Yes | +| Action | `Allow`, `Block`, `Log`, `Redirect` (platform-dependent) | Yes | + +## Match Conditions + +Each match condition specifies a request attribute to inspect, an operator, and match values. + +### Match variables + +| Variable | Description | Example use | +|----------|-------------|-------------| +| `RemoteAddr` | Client IP address | IP-based allow/block lists | +| `RequestMethod` | HTTP method | Block PUT/DELETE on production | +| `RequestUri` | Full request URI | Block specific URL patterns | +| `RequestHeaders` | HTTP request headers (specify header name) | Match on User-Agent, Referer | +| `RequestBody` | Request body content | Match body patterns | +| `RequestCookies` | Cookie values (specify cookie name) | Inspect session cookies | +| `QueryString` | Query string content | Block injection in query params | +| `PostArgs` | POST body parameters (specify param name) | Inspect form fields | +| `SocketAddr` | Source socket IP (the direct connection IP, may differ from RemoteAddr behind a proxy) | Front Door-specific | + +### Operators + +| Operator | Description | +|----------|-------------| +| `IPMatch` | IP address or CIDR range match | +| `GeoMatch` | Country/region code match (ISO 3166-1 alpha-2) | +| `Equal` | Exact string match | +| `Contains` | Substring match | +| `BeginsWith` | String prefix match | +| `EndsWith` | String suffix match | +| `Regex` | Regular expression match | +| `LessThan` | Numeric comparison | +| `GreaterThan` | Numeric comparison | +| `LessThanOrEqual` | Numeric comparison | +| `GreaterThanOrEqual` | Numeric comparison | +| `Any` | Always matches (use with negation) | + +### Transforms + +Apply transforms to the match variable before evaluation: + +| Transform | Description | +|-----------|-------------| +| `Lowercase` | Convert to lowercase | +| `Uppercase` | Convert to uppercase | +| `Trim` | Remove leading/trailing whitespace | +| `UrlDecode` | URL-decode the value | +| `UrlEncode` | URL-encode the value | +| `RemoveNulls` | Remove null bytes | +| `HtmlEntityDecode` | Decode HTML entities | + +## Common Custom Rule Patterns + +### 1. IP-based Allow List (allow only known IPs) + +```bash +# Application Gateway WAF +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "AllowKnownIPs" \ + --priority 5 \ + --action Allow \ + --rule-type MatchRule + +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "AllowKnownIPs" \ + --match-variables RemoteAddr \ + --operator IPMatch \ + --values "198.51.100.0/24" "203.0.113.10" +``` + +### 2. IP-based Block List + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "BlockBadIPs" \ + --priority 10 \ + --action Block \ + --rule-type MatchRule + +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "BlockBadIPs" \ + --match-variables RemoteAddr \ + --operator IPMatch \ + --values "192.0.2.0/24" "198.51.100.50" +``` + +### 3. Geo-Filtering (block traffic from specific countries) + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "GeoBlock" \ + --priority 15 \ + --action Block \ + --rule-type MatchRule + +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "GeoBlock" \ + --match-variables RemoteAddr \ + --operator GeoMatch \ + --values "CN" "RU" "KP" +``` + +### 4. Block specific User-Agents + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "BlockBadUA" \ + --priority 20 \ + --action Block \ + --rule-type MatchRule + +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "BlockBadUA" \ + --match-variables "RequestHeaders['User-Agent']" \ + --operator Contains \ + --values "sqlmap" "nikto" "nmap" \ + --transforms Lowercase +``` + +### 5. Block access to admin paths + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "BlockAdminPaths" \ + --priority 25 \ + --action Block \ + --rule-type MatchRule + +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "BlockAdminPaths" \ + --match-variables RequestUri \ + --operator Contains \ + --values "/admin" "/wp-admin" "/phpmyadmin" \ + --transforms Lowercase +``` + +## Rate Limiting Rules + +Rate limiting rules restrict the number of requests from a source within a time window. + +### Application Gateway WAF rate limiting + +```bash +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "RateLimitAll" \ + --priority 30 \ + --action Block \ + --rule-type RateLimitRule \ + --rate-limit-threshold 500 \ + --rate-limit-duration FiveMins \ + --group-by-user-session "ClientAddr" +``` + +### Front Door WAF rate limiting + +```bash +az network front-door waf-policy rule create \ + --policy-name \ + --resource-group \ + --name "RateLimitAPI" \ + --priority 10 \ + --action Block \ + --rule-type RateLimitRule \ + --rate-limit-threshold 100 \ + --rate-limit-duration 1 +``` + +### Rate limiting configuration + +| Parameter | Application Gateway | Front Door | +|-----------|-------------------|------------| +| Threshold | Requests per window | Requests per window | +| Duration | `OneMin` or `FiveMins` | `1` (1 minute) or `5` (5 minutes) | +| Group by | `ClientAddr`, `GeoLocation`, or `None` | `SocketAddr` by default | +| Scope | Per Application Gateway | Per Front Door edge POP | + +### Rate limiting with path scoping + +Rate limit only specific endpoints (e.g., login API): + +```bash +# Create rate limit rule +az network application-gateway waf-policy custom-rule create \ + --policy-name \ + --resource-group \ + --name "RateLimitLogin" \ + --priority 35 \ + --action Block \ + --rule-type RateLimitRule \ + --rate-limit-threshold 20 \ + --rate-limit-duration OneMin \ + --group-by-user-session "ClientAddr" + +# Add match condition to scope to /api/login +az network application-gateway waf-policy custom-rule match-condition add \ + --policy-name \ + --resource-group \ + --name "RateLimitLogin" \ + --match-variables RequestUri \ + --operator Contains \ + --values "/api/login" \ + --transforms Lowercase +``` + +## Platform Differences + +| Feature | Application Gateway WAF | Front Door WAF | +|---------|------------------------|----------------| +| Actions | Allow, Block, Log | Allow, Block, Log, Redirect | +| Redirect action | Not supported | Redirect to a URL with custom status code | +| Max custom rules | 100 per policy | 100 per policy | +| Rate limit group-by | ClientAddr, GeoLocation, None | SocketAddr | +| Match variables | RemoteAddr, RequestHeaders, RequestUri, etc. | Same + SocketAddr | +| Negation | Supported (`--negate true`) | Supported | + +## Best Practices + +1. **Use low priority numbers for Allow rules** — allow list rules should have the lowest priority so trusted traffic bypasses all other checks +2. **Combine multiple match conditions** for precision — conditions within a rule are AND'd; for OR logic, create separate rules +3. **Test custom rules in Detection mode first** — verify match behavior before switching to Prevention +4. **Use transforms** (especially `Lowercase`) to prevent case-based evasion +5. **Rate limit login and API endpoints** — these are the most common targets for brute-force and credential-stuffing attacks +6. **Regularly review custom rules** — remove rules for IPs that are no longer relevant; keep the rule set clean +7. **Document each custom rule's purpose** — use descriptive names and maintain an external record of why each rule exists + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| Custom rule not matching | Transform not applied; case mismatch | Add `Lowercase` transform | +| Allow rule not bypassing managed rules | Priority too high (higher number) | Set Allow rule to the lowest priority number | +| Rate limit not triggering | Threshold too high or wrong duration | Reduce threshold or adjust duration | +| Geo-filter blocking wrong countries | Country code typo | Verify ISO 3166-1 alpha-2 codes | +| Rule matching too broadly | Match condition too general | Narrow with additional conditions or more specific operators | + +## Related + +- [waf-modes.md](waf-modes.md) — Testing custom rules in Detection mode +- [exclusions.md](exclusions.md) — Exclusions for managed rules (not custom rules) +- [managed-rules.md](managed-rules.md) — Managed rules evaluated after custom rules +- [Custom rules documentation](https://learn.microsoft.com/azure/web-application-firewall/ag/custom-waf-rules-overview) diff --git a/plugin/skills/azure-waf/references/exclusions.md b/plugin/skills/azure-waf/references/exclusions.md new file mode 100644 index 000000000..7adce0110 --- /dev/null +++ b/plugin/skills/azure-waf/references/exclusions.md @@ -0,0 +1,247 @@ +# WAF Exclusion Lists + +WAF exclusions allow specific request attributes to bypass managed rule evaluation, eliminating false positives without disabling rules entirely. Exclusions are the preferred approach to handle false positives because they maintain protection for all other traffic. + +## Exclusion Types + +### Global exclusions +Apply to **all** managed rules. The specified request attribute is not inspected by any managed rule. + +### Per-rule exclusions +Apply to a **specific** managed rule or rule group. The request attribute is excluded only from that rule's evaluation — all other rules still inspect it. + +**Always prefer per-rule exclusions** — they minimize the security surface exposed by the exclusion. + +## Exclusion Match Variables + +| Match Variable | Description | Example | +|----------------|-------------|---------| +| `RequestHeaderNames` | HTTP request header name | Exclude `X-Custom-Token` header | +| `RequestCookieNames` | Cookie name | Exclude `session_id` cookie | +| `RequestArgNames` | Query string parameter name | Exclude `search` query parameter | +| `RequestBodyPostArgNames` | POST body parameter name | Exclude `comment_text` form field | +| `RequestBodyJsonArgNames` | JSON body field name | Exclude `data.payload` JSON field | + +## Selector Match Operators + +| Operator | Behavior | Example | +|----------|----------|---------| +| `Equals` | Exact match on the attribute name | Header name exactly equals `Authorization` | +| `Contains` | Substring match on the attribute name | Any header containing `Token` | +| `StartsWith` | Prefix match on the attribute name | Any cookie starting with `__utm` | +| `EndsWith` | Suffix match on the attribute name | Any parameter ending with `_raw` | +| `EqualsAny` | Matches any attribute of that type | All request headers (use cautiously) | + +## Configuring Global Exclusions + +### Application Gateway WAF + +```bash +# Exclude a specific header from all managed rules +az network application-gateway waf-policy managed-rule exclusion add \ + --policy-name \ + --resource-group \ + --match-variable RequestHeaderNames \ + --selector-match-operator Equals \ + --selector "X-Custom-Auth" + +# Exclude a query parameter from all managed rules +az network application-gateway waf-policy managed-rule exclusion add \ + --policy-name \ + --resource-group \ + --match-variable RequestArgNames \ + --selector-match-operator Equals \ + --selector "returnUrl" + +# Exclude a POST body field from all managed rules +az network application-gateway waf-policy managed-rule exclusion add \ + --policy-name \ + --resource-group \ + --match-variable RequestBodyPostArgNames \ + --selector-match-operator Equals \ + --selector "article_body" +``` + +### Front Door WAF + +Front Door exclusions are configured similarly but through the WAF policy managed-rule settings. Use the portal or ARM template for per-rule exclusions on Front Door. + +## Configuring Per-Rule Exclusions + +Per-rule exclusions target a specific rule ID so the excluded attribute is only bypassed for that rule. + +### Application Gateway WAF per-rule exclusion + +```bash +# Exclude a specific header from rule 942130 only +az network application-gateway waf-policy managed-rule exclusion rule-set add \ + --policy-name \ + --resource-group \ + --match-variable RequestHeaderNames \ + --selector-match-operator Equals \ + --selector "X-Forwarded-Host" \ + --type OWASP \ + --version 3.2 \ + --group-name REQUEST-942-APPLICATION-ATTACK-SQLI \ + --rule-ids 942130 +``` + +### Per-rule-group exclusion + +```bash +# Exclude a field from all rules in the SQLI group +az network application-gateway waf-policy managed-rule exclusion rule-set add \ + --policy-name \ + --resource-group \ + --match-variable RequestBodyPostArgNames \ + --selector-match-operator Equals \ + --selector "sql_query_field" \ + --type OWASP \ + --version 3.2 \ + --group-name REQUEST-942-APPLICATION-ATTACK-SQLI +``` + +## Common False Positive Patterns and Resolutions + +### 1. SQL-like content in form fields + +**Scenario**: A CMS or admin panel has a text field where users enter content that looks like SQL (e.g., `SELECT * FROM users`). + +**Triggering rules**: 942100, 942110, 942130, 942150, 942180, 942200, 942210, 942260, 942340, 942370, 942430 + +**Resolution**: +```bash +az network application-gateway waf-policy managed-rule exclusion rule-set add \ + --policy-name \ + --resource-group \ + --match-variable RequestBodyPostArgNames \ + --selector-match-operator Equals \ + --selector "content_body" \ + --type OWASP --version 3.2 \ + --group-name REQUEST-942-APPLICATION-ATTACK-SQLI +``` + +### 2. Rich text / HTML in request body + +**Scenario**: A WYSIWYG editor sends HTML content that triggers XSS rules. + +**Triggering rules**: 941100, 941110, 941120, 941130, 941140, 941150, 941160, 941170, 941180, 941320 + +**Resolution**: +```bash +az network application-gateway waf-policy managed-rule exclusion rule-set add \ + --policy-name \ + --resource-group \ + --match-variable RequestBodyPostArgNames \ + --selector-match-operator Equals \ + --selector "editor_content" \ + --type OWASP --version 3.2 \ + --group-name REQUEST-941-APPLICATION-ATTACK-XSS +``` + +### 3. Encoded tokens in headers + +**Scenario**: Custom authentication headers contain Base64 or JWT tokens that match SQLi or XSS patterns. + +**Triggering rules**: Various across SQLI and XSS groups + +**Resolution**: +```bash +az network application-gateway waf-policy managed-rule exclusion add \ + --policy-name \ + --resource-group \ + --match-variable RequestHeaderNames \ + --selector-match-operator Equals \ + --selector "Authorization" +``` + +### 4. File paths in URL parameters + +**Scenario**: API accepts file paths as query parameters, triggering LFI rules. + +**Triggering rules**: 930100, 930110, 930120, 930130 + +**Resolution**: +```bash +az network application-gateway waf-policy managed-rule exclusion rule-set add \ + --policy-name \ + --resource-group \ + --match-variable RequestArgNames \ + --selector-match-operator Equals \ + --selector "filePath" \ + --type OWASP --version 3.2 \ + --group-name REQUEST-930-APPLICATION-ATTACK-LFI +``` + +### 5. API bodies with command-like strings + +**Scenario**: DevOps or infrastructure APIs accept command strings that trigger RCE rules. + +**Triggering rules**: 932100, 932105, 932106, 932110, 932115, 932150 + +**Resolution**: +```bash +az network application-gateway waf-policy managed-rule exclusion rule-set add \ + --policy-name \ + --resource-group \ + --match-variable RequestBodyJsonArgNames \ + --selector-match-operator Equals \ + --selector "command" \ + --type OWASP --version 3.2 \ + --group-name REQUEST-932-APPLICATION-ATTACK-RCE +``` + +## Diagnosing What to Exclude + +### Step 1: Find the triggering rule + +Query WAF logs to identify the rule ID and the matched request attribute: + +```kusto +AzureDiagnostics +| where Category == "ApplicationGatewayFirewallLog" +| where action_s == "Blocked" or action_s == "Detected" +| project TimeGenerated, ruleId_s, ruleGroup_s, details_message_s, + details_data_s, requestUri_s, clientIp_s +| order by TimeGenerated desc +| take 50 +``` + +The `details_message_s` field shows what part of the request matched (e.g., "Matched Data: SELECT found within ARGS:search_query"). + +### Step 2: Identify the match variable and selector + +From the log: +- `ARGS:search_query` → Match variable: `RequestArgNames`, Selector: `search_query` +- `REQUEST_HEADERS:X-Custom-Header` → Match variable: `RequestHeaderNames`, Selector: `X-Custom-Header` +- `REQUEST_BODY` → Match variable: `RequestBodyPostArgNames` or `RequestBodyJsonArgNames` +- `REQUEST_COOKIES:session` → Match variable: `RequestCookieNames`, Selector: `session` + +### Step 3: Create the narrowest possible exclusion + +1. First try per-rule exclusion (specific rule ID) +2. If multiple rules in the same group trigger, try per-rule-group exclusion +3. Only use global exclusion as a last resort + +## Best Practices + +1. **Always use per-rule exclusions over global exclusions** — global exclusions remove the attribute from ALL rule evaluation +2. **Use `Equals` operator by default** — `Contains`, `StartsWith`, and `EndsWith` may inadvertently exclude more attributes than intended +3. **Document every exclusion** — record the rule ID, the reason for the exclusion, and the date it was added +4. **Review exclusions quarterly** — applications change; exclusions that were needed may no longer be relevant +5. **Test exclusions in Detection mode** — verify the false positive disappears before switching to Prevention +6. **Never use `EqualsAny`** in production unless absolutely necessary — it excludes all attributes of that type from the rule + +## Limits + +| Resource | Limit | +|----------|-------| +| Global exclusions per policy | 100 | +| Per-rule exclusions per policy | 100 per rule override | +| Maximum selector length | 256 characters | + +## Related + +- [waf-modes.md](waf-modes.md) — Using Detection mode to identify false positives +- [managed-rules.md](managed-rules.md) — Rule groups and IDs referenced in exclusions +- [WAF exclusion documentation](https://learn.microsoft.com/azure/web-application-firewall/ag/application-gateway-waf-configuration) diff --git a/plugin/skills/azure-waf/references/managed-rules.md b/plugin/skills/azure-waf/references/managed-rules.md new file mode 100644 index 000000000..787307fb2 --- /dev/null +++ b/plugin/skills/azure-waf/references/managed-rules.md @@ -0,0 +1,166 @@ +# WAF Managed Rule Sets + +Azure WAF provides Microsoft-curated managed rule sets that protect against the OWASP Top 10 and other common web vulnerabilities. The rule sets differ between Application Gateway WAF and Front Door WAF. + +## Rule Sets by Platform + +### Application Gateway WAF: OWASP Core Rule Set (CRS) + +| Version | Status | Scoring model | Notes | +|---------|--------|---------------|-------| +| CRS 3.2 | **Recommended** | Anomaly scoring | Latest, fewest false positives, best performance | +| CRS 3.1 | Supported | Anomaly scoring | Stable, widely deployed | +| CRS 3.0 | Supported | Anomaly scoring | Older, more false positives | +| CRS 2.2.9 | Deprecated | First-match | Legacy — migrate to 3.2 | + +### Front Door WAF: Microsoft Default Rule Set (DRS) + +| Version | Status | Scoring model | Notes | +|---------|--------|---------------|-------| +| DRS 2.1 | **Recommended** | Per-rule action | Latest, optimized for Front Door | +| DRS 2.0 | Supported | Per-rule action | Stable | +| DRS 1.1 | Supported | Per-rule action | Older | +| DRS 1.0 | Deprecated | Per-rule action | Legacy — migrate to 2.1 | + +## Rule Groups (CRS 3.2) + +Each rule set is organized into rule groups that target specific attack categories: + +| Rule Group | Rule ID range | What it detects | +|------------|---------------|-----------------| +| REQUEST-911-METHOD-ENFORCEMENT | 911xxx | Unusual HTTP methods (PUT, DELETE, PATCH, etc.) | +| REQUEST-913-SCANNER-DETECTION | 913xxx | Known scanner/bot user agents and patterns | +| REQUEST-920-PROTOCOL-ENFORCEMENT | 920xxx | Protocol violations (missing Host header, malformed requests) | +| REQUEST-921-PROTOCOL-ATTACK | 921xxx | HTTP request smuggling, response splitting | +| REQUEST-930-APPLICATION-ATTACK-LFI | 930xxx | Local file inclusion (path traversal like `../../etc/passwd`) | +| REQUEST-931-APPLICATION-ATTACK-RFI | 931xxx | Remote file inclusion | +| REQUEST-932-APPLICATION-ATTACK-RCE | 932xxx | Remote code execution (OS command injection) | +| REQUEST-933-APPLICATION-ATTACK-PHP | 933xxx | PHP-specific injection attacks | +| REQUEST-941-APPLICATION-ATTACK-XSS | 941xxx | Cross-site scripting (reflected, stored, DOM) | +| REQUEST-942-APPLICATION-ATTACK-SQLI | 942xxx | SQL injection (all database types) | +| REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION | 943xxx | Session fixation attacks | +| REQUEST-944-APPLICATION-ATTACK-JAVA | 944xxx | Java-specific attacks (Log4j, deserialization) | +| General | Various | Catch-all rules not fitting other categories | + +## DRS Rule Groups (DRS 2.1) + +DRS uses similar categories with Microsoft-specific optimizations: + +| Rule Group | What it detects | +|------------|-----------------| +| General | Generic attack patterns | +| METHOD-ENFORCEMENT | Unusual HTTP methods | +| PROTOCOL-ENFORCEMENT | Protocol violations | +| PROTOCOL-ATTACK | HTTP smuggling, splitting | +| LFI | Local file inclusion | +| RFI | Remote file inclusion | +| RCE | Remote code execution | +| PHP | PHP injection | +| NODEJS | Node.js-specific attacks | +| XSS | Cross-site scripting | +| SQLI | SQL injection | +| SESSION-FIXATION | Session fixation | +| JAVA | Java attacks (Log4j) | +| MS-ThreatIntel-WebShells | Known web shell patterns | +| MS-ThreatIntel-AppSec | Microsoft threat intelligence application security | +| MS-ThreatIntel-SQLI | Microsoft threat intelligence SQL injection | +| MS-ThreatIntel-CVEs | Known CVE exploits | + +The `MS-ThreatIntel-*` groups are unique to DRS and powered by Microsoft's threat intelligence — they have no CRS equivalent. + +## Configuring Managed Rules + +### Application Gateway: Add CRS 3.2 + +```bash +az network application-gateway waf-policy managed-rule rule-set add \ + --policy-name \ + --resource-group \ + --type OWASP \ + --version 3.2 +``` + +### Disable a specific rule + +```bash +az network application-gateway waf-policy managed-rule rule-set update \ + --policy-name \ + --resource-group \ + --type OWASP \ + --version 3.2 \ + --group-name REQUEST-942-APPLICATION-ATTACK-SQLI \ + --rules 942130 \ + --state Disabled +``` + +### Change a rule's action (DRS 2.1 on Front Door) + +```bash +# Front Door DRS: Override a rule action to Log instead of Block +az network front-door waf-policy managed-rule-definition list -o table +``` + +For Front Door, use the portal or ARM template to override individual rule actions in DRS. + +## Upgrading Rule Set Versions + +### CRS upgrade on Application Gateway + +```bash +# Remove old rule set +az network application-gateway waf-policy managed-rule rule-set remove \ + --policy-name \ + --resource-group \ + --type OWASP \ + --version 3.1 + +# Add new rule set +az network application-gateway waf-policy managed-rule rule-set add \ + --policy-name \ + --resource-group \ + --type OWASP \ + --version 3.2 +``` + +**Before upgrading**: +1. Switch to Detection mode +2. Apply the new rule set version +3. Run for 1–2 weeks to catch new false positives introduced by the new version +4. Add exclusions as needed +5. Switch back to Prevention mode + +### What changes between versions + +- New rules added for emerging threats +- Existing rules may be tuned (fewer false positives) +- Rule IDs may change — check if disabled rules still exist in the new version +- Anomaly score thresholds may be adjusted + +## Common False Positive Rule Groups + +These rule groups most frequently trigger false positives and often need tuning: + +| Rule Group | Common trigger | Resolution | +|------------|---------------|------------| +| SQLI (942xxx) | Application forms with SQL-like syntax in inputs | Add exclusion for the specific form field | +| XSS (941xxx) | Rich text editors, HTML content in request body | Add exclusion for the editor's request field | +| PROTOCOL-ENFORCEMENT (920xxx) | Missing or unusual headers from API clients | Add exclusion or disable specific rules | +| LFI (930xxx) | File paths in URL parameters (e.g., `/api/files/path/to/file`) | Add exclusion for the specific URL pattern | +| RCE (932xxx) | Application fields with OS-command-like syntax | Add per-rule exclusion for the field | + +## Best Practices + +1. **Always use the latest rule set version** — newer versions have better detection and fewer false positives +2. **Prefer per-rule exclusions over disabling rules** — maintain protection while allowing specific request attributes through +3. **Never disable entire rule groups in production** — disable individual rules if needed +4. **Monitor rule hit counts** to understand your application's attack surface and identify over-triggering rules +5. **Subscribe to Azure WAF rule set update notifications** to stay informed about new rules and changes +6. **Test rule set upgrades in Detection mode** before enabling Prevention mode + +## Related + +- [exclusions.md](exclusions.md) — How to configure exclusions for false positive rules +- [waf-modes.md](waf-modes.md) — Detection vs Prevention mode +- [custom-rules.md](custom-rules.md) — User-defined rules that run before managed rules +- [OWASP CRS documentation](https://learn.microsoft.com/azure/web-application-firewall/ag/application-gateway-crs-rulegroups-rules) +- [DRS documentation](https://learn.microsoft.com/azure/web-application-firewall/afds/waf-front-door-drs) diff --git a/plugin/skills/azure-waf/references/waf-modes.md b/plugin/skills/azure-waf/references/waf-modes.md new file mode 100644 index 000000000..d40c92675 --- /dev/null +++ b/plugin/skills/azure-waf/references/waf-modes.md @@ -0,0 +1,200 @@ +# WAF Detection Mode vs Prevention Mode + +Azure WAF operates in one of two modes that determine how it handles requests matching rule conditions. Choosing the right mode — and knowing when to transition — is critical for balancing security and availability. + +## Mode Comparison + +| Aspect | Detection Mode | Prevention Mode | +|--------|---------------|-----------------| +| Matching requests | Logged only | Blocked and logged | +| HTTP response to client | Request passes through to backend | 403 Forbidden (or custom response) | +| Impact on availability | None — no requests are blocked | Potential — false positives block legitimate traffic | +| Use case | Tuning, initial deployment, testing | Production protection | +| Risk | Attacks are logged but not stopped | False positives disrupt real users | + +## Detection Mode + +In Detection mode, WAF evaluates all incoming requests against managed and custom rules, logs matches, but **does not block any traffic**. The request is forwarded to the backend regardless of rule matches. + +### When to use Detection mode +- **Initial WAF deployment**: Run Detection mode for the first 1–2 weeks in production to observe which rules fire and identify false positives before blocking anything +- **After rule changes**: When adding new managed rule sets or custom rules, temporarily switch to Detection for validation +- **After application changes**: When the application changes its request patterns (new APIs, new headers), run Detection to verify rules still match correctly +- **Troubleshooting**: When investigating whether WAF is causing application issues + +### What gets logged +Every matched rule generates a log entry containing: +- Rule ID and rule group +- Match details (which part of the request matched: URI, headers, body, etc.) +- Action taken (Detected) +- Request metadata (client IP, URI, user agent) + +### Log query example (Log Analytics) + +```kusto +AzureDiagnostics +| where ResourceProvider == "MICROSOFT.NETWORK" +| where Category == "ApplicationGatewayFirewallLog" +| where action_s == "Detected" +| summarize count() by ruleId_s, ruleGroup_s +| order by count_ desc +``` + +For Front Door: +```kusto +AzureDiagnostics +| where ResourceProvider == "MICROSOFT.CDN" +| where Category == "FrontDoorWebApplicationFirewallLog" +| where action_s == "Log" +| summarize count() by ruleName_s +| order by count_ desc +``` + +## Prevention Mode + +In Prevention mode, WAF evaluates requests and **actively blocks** those matching rules with a Block action. Blocked requests receive a 403 Forbidden response (customizable) and are logged. + +### When to use Prevention mode +- **Production protection**: After tuning is complete and false positives are resolved +- **Compliance requirements**: When regulatory requirements mandate active blocking of threats +- **Known-good configuration**: When the WAF rule set has been validated against production traffic + +### Custom error responses + +Application Gateway WAF allows customizing the block response: + +```bash +# Set a custom response body for blocked requests +az network application-gateway waf-policy policy-setting update \ + --policy-name \ + --resource-group \ + --mode Prevention \ + --state Enabled \ + --custom-block-response-status-code 403 \ + --custom-block-response-body "PGh0bWw+QmxvY2tlZCBieSBXQUY8L2h0bWw+" +``` + +The response body is Base64-encoded. This example encodes `Blocked by WAF`. + +## Transitioning from Detection to Prevention + +Follow this workflow to safely transition: + +### Step 1: Enable Detection mode with logging + +```bash +# Application Gateway WAF +az network application-gateway waf-policy policy-setting update \ + --policy-name \ + --resource-group \ + --state Enabled \ + --mode Detection + +# Front Door WAF +az network front-door waf-policy update \ + --name \ + --resource-group \ + --mode Detection +``` + +### Step 2: Run for at least 1–2 weeks in production + +During this period: +- Review WAF logs daily for matched rules +- Identify false positives — legitimate requests that match rules +- Correlate rule IDs with request patterns to understand what is triggering + +### Step 3: Configure exclusions for false positives + +For each false positive: +1. Identify the rule ID that triggered +2. Determine which request attribute caused the match (header, cookie, body field) +3. Add a per-rule exclusion (preferred) or global exclusion + +See [exclusions.md](exclusions.md) for detailed exclusion configuration. + +### Step 4: Disable overly aggressive rules (last resort) + +If exclusions do not resolve a false positive: + +```bash +# Disable a specific rule +az network application-gateway waf-policy managed-rule rule-set update \ + --policy-name \ + --resource-group \ + --type OWASP \ + --version 3.2 \ + --group-name REQUEST-942-APPLICATION-ATTACK-SQLI \ + --rules 942130 \ + --state Disabled +``` + +### Step 5: Switch to Prevention mode + +```bash +# Application Gateway WAF +az network application-gateway waf-policy policy-setting update \ + --policy-name \ + --resource-group \ + --mode Prevention + +# Front Door WAF +az network front-door waf-policy update \ + --name \ + --resource-group \ + --mode Prevention +``` + +### Step 6: Monitor closely for 48–72 hours + +After switching: +- Watch for increased 403 responses +- Monitor application availability and error rates +- Have a rollback plan ready (switch back to Detection) +- Check WAF logs for blocked requests that might be legitimate + +## Anomaly Scoring (Application Gateway CRS 3.2+) + +OWASP CRS 3.2 and later on Application Gateway use anomaly scoring: + +- Each matching rule adds a score (Critical: 5, Error: 4, Warning: 3, Notice: 2) +- The request is blocked only if the total anomaly score exceeds the threshold (default: 5) +- This reduces false positives compared to the "first match blocks" model in older CRS versions + +To adjust the anomaly score threshold: + +```bash +az network application-gateway waf-policy policy-setting update \ + --policy-name \ + --resource-group \ + --mode Prevention \ + --request-body-check true \ + --max-request-body-size-in-kb 128 +``` + +> **Note**: Front Door DRS does not use anomaly scoring — each rule match independently determines the action. + +## Platform Differences + +| Behavior | Application Gateway WAF | Front Door WAF | +|----------|------------------------|----------------| +| Scoring model | Anomaly scoring (CRS 3.2+) | Per-rule action (no scoring) | +| Detection mode logging | `action_s = "Detected"` | `action_s = "Log"` | +| Prevention block response | 403 (customizable body) | 403 (customizable body) | +| Mode change propagation | Immediate | ~1-2 minutes (global edge deployment) | +| Body inspection | Up to 128 KB (configurable) | Up to 128 KB | + +## Common Troubleshooting + +| Symptom | Likely cause | Resolution | +|---------|-------------|------------| +| False positives blocking users after switching to Prevention | Insufficient tuning in Detection mode | Switch back to Detection; add exclusions for false positives | +| No WAF logs appearing | Diagnostic logs not configured | Enable WAF diagnostic logs to Log Analytics | +| Mode change not taking effect | Policy not associated with gateway/Front Door | Verify WAF policy association | +| Legitimate API calls blocked | Request body or headers matching SQL/XSS patterns | Add per-rule exclusions for the specific request attributes | + +## Related + +- [exclusions.md](exclusions.md) — How to configure exclusions for false positives +- [managed-rules.md](managed-rules.md) — Rule set versions and differences +- [WAF monitoring and logging](https://learn.microsoft.com/azure/web-application-firewall/ag/application-gateway-waf-metrics)