From abb2092428ea2588fb911c474e4554b17f7d569c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 2 Apr 2026 11:16:45 +0000 Subject: [PATCH 1/3] Sync plugin files from GitHub-Copilot-for-Azure --- .../azure-skills/.claude-plugin/plugin.json | 2 +- .../plugins/azure-skills/.plugin/plugin.json | 2 +- .../skills/azure-cost-optimization/SKILL.md | 425 ------------------ .../redis-detailed-cache-analysis.md | 96 ---- .../redis-subscription-level-report.md | 30 -- .../azure-skills/skills/azure-cost/SKILL.md | 138 ++++++ .../cost-forecast/error-handling.md | 48 ++ .../azure-cost/cost-forecast/examples.md | 125 ++++++ .../azure-cost/cost-forecast/guardrails.md | 78 ++++ .../cost-forecast/request-body-schema.md | 111 +++++ .../azure-cost/cost-forecast/workflow.md | 122 +++++ .../cost-optimization}/auth-best-practices.md | 0 .../cost-optimization}/azure-aks-anomalies.md | 0 .../azure-aks-cost-addon.md | 0 .../cost-optimization}/azure-quick-review.md | 0 .../azure-resource-graph.md | 204 ++++----- .../cost-optimization/report-template.md | 69 +++ .../azure-resource-manager-redis-dotnet.md | 0 .../services/redis/azure-cache-for-redis.md} | 8 +- .../azure-cost/cost-optimization/workflow.md | 188 ++++++++ .../cost-query/dimensions-by-scope.md | 182 ++++++++ .../azure-cost/cost-query/error-handling.md | 67 +++ .../skills/azure-cost/cost-query/examples.md | 127 ++++++ .../azure-cost/cost-query/guardrails.md | 164 +++++++ .../cost-query/request-body-schema.md | 170 +++++++ .../skills/azure-cost/cost-query/workflow.md | 139 ++++++ .../azure-skills/skills/azure-deploy/SKILL.md | 2 +- .../references/recipes/azd/errors.md | 27 ++ .../references/troubleshooting.md | 4 + .../skills/azure-prepare/SKILL.md | 4 +- .../references/recipes/azd/iac-rules.md | 19 +- .../references/recipes/bicep/patterns.md | 13 +- .../azure-prepare/references/research.md | 2 +- .../references/specialized-routing.md | 2 +- .../skills/azure-resource-lookup/SKILL.md | 2 +- 35 files changed, 1901 insertions(+), 669 deletions(-) delete mode 100644 .github/plugins/azure-skills/skills/azure-cost-optimization/SKILL.md delete mode 100644 .github/plugins/azure-skills/skills/azure-cost-optimization/templates/redis-detailed-cache-analysis.md delete mode 100644 .github/plugins/azure-skills/skills/azure-cost-optimization/templates/redis-subscription-level-report.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/SKILL.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-forecast/error-handling.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-forecast/examples.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-forecast/guardrails.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-forecast/request-body-schema.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-forecast/workflow.md rename .github/plugins/azure-skills/skills/{azure-cost-optimization/references => azure-cost/cost-optimization}/auth-best-practices.md (100%) rename .github/plugins/azure-skills/skills/{azure-cost-optimization/references => azure-cost/cost-optimization}/azure-aks-anomalies.md (100%) rename .github/plugins/azure-skills/skills/{azure-cost-optimization/references => azure-cost/cost-optimization}/azure-aks-cost-addon.md (100%) rename .github/plugins/azure-skills/skills/{azure-cost-optimization/references => azure-cost/cost-optimization}/azure-quick-review.md (100%) rename .github/plugins/azure-skills/skills/{azure-cost-optimization/references => azure-cost/cost-optimization}/azure-resource-graph.md (95%) create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-optimization/report-template.md rename .github/plugins/azure-skills/skills/{azure-cost-optimization/references => azure-cost/cost-optimization}/sdk/azure-resource-manager-redis-dotnet.md (100%) rename .github/plugins/azure-skills/skills/{azure-cost-optimization/references/azure-redis.md => azure-cost/cost-optimization/services/redis/azure-cache-for-redis.md} (84%) create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-optimization/workflow.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-query/dimensions-by-scope.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-query/error-handling.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-query/examples.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-query/guardrails.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-query/request-body-schema.md create mode 100644 .github/plugins/azure-skills/skills/azure-cost/cost-query/workflow.md diff --git a/.github/plugins/azure-skills/.claude-plugin/plugin.json b/.github/plugins/azure-skills/.claude-plugin/plugin.json index 60fc0f4b..18da3b72 100644 --- a/.github/plugins/azure-skills/.claude-plugin/plugin.json +++ b/.github/plugins/azure-skills/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "azure", "description": "Microsoft Azure MCP integration for cloud resource management, deployments, and Azure services. Manage your Azure infrastructure, monitor applications, and deploy resources directly from Claude Code.", - "version": "1.0.6", + "version": "1.0.7", "author": { "name": "Microsoft", "url": "https://www.microsoft.com" diff --git a/.github/plugins/azure-skills/.plugin/plugin.json b/.github/plugins/azure-skills/.plugin/plugin.json index 2e487a8d..d9f842c4 100644 --- a/.github/plugins/azure-skills/.plugin/plugin.json +++ b/.github/plugins/azure-skills/.plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "azure", "description": "Microsoft Azure MCP integration for cloud resource management, deployments, and Azure services. Manage your Azure infrastructure, monitor applications, and deploy resources directly from your development environment.", - "version": "1.0.6", + "version": "1.0.7", "author": { "name": "Microsoft", "url": "https://www.microsoft.com" diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/SKILL.md b/.github/plugins/azure-skills/skills/azure-cost-optimization/SKILL.md deleted file mode 100644 index f86d6edc..00000000 --- a/.github/plugins/azure-skills/skills/azure-cost-optimization/SKILL.md +++ /dev/null @@ -1,425 +0,0 @@ ---- -name: azure-cost-optimization -description: "Identify Azure cost savings from usage and spending data. USE FOR: optimize Azure costs, reduce Azure spending/expenses, analyze Azure costs, find cost savings, generate cost optimization report, identify orphaned resources to delete, rightsize VMs, reduce waste, optimize Redis costs, optimize storage costs, AKS cost analysis add-on, namespace cost, cost spike, anomaly, budget alert, AKS cost visibility. DO NOT USE FOR: deploying resources (use azure-deploy), general Azure diagnostics (use azure-diagnostics), security issues (use azure-security)" -license: MIT -metadata: - author: Microsoft - version: "1.0.2" ---- - -# Azure Cost Optimization Skill - -Analyze Azure subscriptions to identify cost savings through orphaned resource cleanup, rightsizing, and optimization recommendations based on actual usage data. - -## When to Use This Skill - -Use this skill when the user asks to: -- Optimize Azure costs or reduce spending -- Analyze Azure subscription for cost savings -- Generate cost optimization report -- Find orphaned or unused resources -- Rightsize Azure VMs, containers, or services -- Identify where they're overspending in Azure -- **Optimize Redis costs specifically** - See [Azure Redis Cost Optimization](./references/azure-redis.md) for Redis-specific analysis - -## Instructions - -Follow these steps in conversation with the user: - -### Step 0: Validate Prerequisites - -Before starting, verify these tools and permissions are available: - -**Required Tools:** -- Azure CLI installed and authenticated (`az login`) -- Azure CLI extensions: `costmanagement`, `resource-graph` -- Azure Quick Review (azqr) installed - See [Azure Quick Review](./references/azure-quick-review.md) for details - -**Required Permissions:** -- Cost Management Reader role -- Monitoring Reader role -- Reader role on subscription/resource group - -**Verification commands:** -```powershell -az --version -az account show -az extension show --name costmanagement -azqr version -``` - -### Step 1: Load Best Practices - -Get Azure cost optimization best practices to inform recommendations: - -```javascript -// Use Azure MCP best practices tool -mcp_azure_mcp_get_azure_bestpractices({ - intent: "Get cost optimization best practices", - command: "get_bestpractices", - parameters: { resource: "cost-optimization", action: "all" } -}) -``` - -### Step 1.5: Redis-Specific Analysis (Conditional) - -**If the user specifically requests Redis cost optimization**, use the specialized Redis skill: - -πŸ“‹ **Reference**: [Azure Redis Cost Optimization](./references/azure-redis.md) - -**When to use Redis-specific analysis:** -- User mentions "Redis", "Azure Cache for Redis", or "Azure Managed Redis" -- Focus is on Redis resource optimization, not general subscription analysis -- User wants Redis-specific recommendations (SKU downgrade, failed caches, etc.) - -**Key capabilities:** -- Interactive subscription filtering (prefix, ID, or "all subscriptions") -- Redis-specific optimization rules (failed caches, oversized tiers, missing tags) -- Pre-built report templates for Redis cost analysis -- Uses `redis_list` command - -**Report templates available:** -- [Subscription-level Redis summary](./templates/redis-subscription-level-report.md) -- [Detailed Redis cache analysis](./templates/redis-detailed-cache-analysis.md) - -> **Note**: For general subscription-wide cost optimization (including Redis), continue with Step 2. For Redis-only focused analysis, follow the instructions in the Redis-specific reference document. -### Step 1.6: Choose Analysis Scope (for Redis-specific analysis) - -**If performing Redis cost optimization**, ask the user to select their analysis scope: - -**Prompt the user with these options:** -1. **Specific Subscription ID** - Analyze a single subscription -2. **Subscription Name** - Use display name instead of ID -3. **Subscription Prefix** - Analyze all subscriptions starting with a prefix (e.g., "CacheTeam") -4. **All My Subscriptions** - Scan all accessible subscriptions -5. **Tenant-wide** - Analyze entire organization - -Wait for user response, then proceed to Step 2. - -### Step 1.7: AKS-Specific Analysis (Conditional) - -**If the user specifically requests AKS cost optimization**, use the specialized AKS reference files: - -**When to use AKS-specific analysis:** -- User mentions "AKS", "Kubernetes", "cluster", "node pool", "pod", or "kubectl" -- User wants to enable the AKS cost analysis add-on or namespace cost visibility -- User reports a cost spike, unusual cluster utilization, or wants budget alerts - -**Tool Selection:** -- **Prefer MCP first**: Use `mcp_azure_mcp_aks` for AKS operations (list clusters, get node pools, inspect configuration) β€” it provides richer metadata and is consistent with AKS skill conventions in this repo -- **Fall back to CLI**: Use `az aks` and `kubectl` only when the specific operation cannot be performed via the MCP surface - -**Reference files (load only what is needed for the request):** -- [Cost Analysis Add-on](./references/azure-aks-cost-addon.md) β€” enable namespace-level cost visibility -- [Anomaly Investigation](./references/azure-aks-anomalies.md) β€” cost spikes, scaling events, budget alerts - -> **Note**: For general subscription-wide cost optimization (including AKS resource groups), continue with Step 2. For AKS-focused analysis, follow the instructions in the relevant reference file above. - -### Step 1.8: Choose Analysis Scope (for AKS-specific analysis) - -**If performing AKS cost optimization**, ask the user to select their analysis scope: - -**Prompt the user with these options:** -1. **Specific Cluster Name** - Analyze a single AKS cluster -2. **Resource Group** - Analyze all clusters in a resource group -3. **Subscription ID** - Analyze all clusters in a subscription -4. **All My Clusters** - Scan all accessible clusters across subscriptions - -Wait for user response before proceeding to Step 2. - -### Step 2: Run Azure Quick Review - -Run azqr to find orphaned resources (immediate cost savings): - -πŸ“‹ **Reference**: [Azure Quick Review](./references/azure-quick-review.md) - Detailed instructions for running azqr scans - -```javascript -// Use Azure MCP extension_azqr tool -extension_azqr({ - subscription: "", - "resource-group": "" // optional -}) -``` - -**What to look for in azqr results:** -- Orphaned resources: unattached disks, unused NICs, idle NAT gateways -- Over-provisioned resources: excessive retention periods, oversized SKUs -- Missing cost tags: resources without proper cost allocation - -> **Note**: The Azure Quick Review reference document includes instructions for creating filter configurations, saving output to the `output/` folder, and interpreting results for cost optimization. - -### Step 3: Discover Resources - -For efficient cross-subscription resource discovery, use Azure Resource Graph. See [Azure Resource Graph Queries](references/azure-resource-graph.md) for orphaned resource detection and cost optimization patterns. - -List all resources in the subscription using Azure MCP tools or CLI: - -```powershell -# Get subscription info -az account show - -# List all resources -az resource list --subscription "" --resource-group "" - -# Use MCP tools for specific services (preferred): -# - Storage accounts, Cosmos DB, Key Vaults: use Azure MCP tools -# - Redis caches: use mcp_azure_mcp_redis tool (see ./references/azure-redis.md) -# - Web apps, VMs, SQL: use az CLI commands -``` - -### Step 4: Query Actual Costs - -Get actual cost data from Azure Cost Management API (last 30 days): - -**Create cost query file:** - -Create `temp/cost-query.json` with: -```json -{ - "type": "ActualCost", - "timeframe": "Custom", - "timePeriod": { - "from": "", - "to": "" - }, - "dataset": { - "granularity": "None", - "aggregation": { - "totalCost": { - "name": "Cost", - "function": "Sum" - } - }, - "grouping": [ - { - "type": "Dimension", - "name": "ResourceId" - } - ] - } -} -``` - -> **Action Required**: Calculate `` (30 days ago) and `` (today) in ISO 8601 format (e.g., `2025-11-03T00:00:00Z`). - -**Execute cost query:** -```powershell -# Create temp folder -New-Item -ItemType Directory -Path "temp" -Force - -# Query using REST API (more reliable than az costmanagement query) -az rest --method post ` - --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.CostManagement/query?api-version=2023-11-01" ` - --body '@temp/cost-query.json' -``` - -**Important:** Save the query results to `output/cost-query-result.json` for audit trail. - -### Step 5: Validate Pricing - -Fetch current pricing from official Azure pricing pages using `fetch_webpage`: - -```javascript -// Validate pricing for key services -fetch_webpage({ - urls: ["https://azure.microsoft.com/en-us/pricing/details/container-apps/"], - query: "pricing tiers and costs" -}) -``` - -**Key services to validate:** -- Container Apps: https://azure.microsoft.com/pricing/details/container-apps/ -- Virtual Machines: https://azure.microsoft.com/pricing/details/virtual-machines/ -- App Service: https://azure.microsoft.com/pricing/details/app-service/ -- Log Analytics: https://azure.microsoft.com/pricing/details/monitor/ - -> **Important**: Check for free tier allowances - many Azure services have generous free limits that may explain $0 costs. - -### Step 6: Collect Utilization Metrics - -Query Azure Monitor for utilization data (last 14 days) to support rightsizing recommendations: - -```powershell -# Calculate dates for last 14 days -$startTime = (Get-Date).AddDays(-14).ToString("yyyy-MM-ddTHH:mm:ssZ") -$endTime = Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ" - -# VM CPU utilization -az monitor metrics list ` - --resource "" ` - --metric "Percentage CPU" ` - --interval PT1H ` - --aggregation Average ` - --start-time $startTime ` - --end-time $endTime - -# App Service Plan utilization -az monitor metrics list ` - --resource "" ` - --metric "CpuTime,Requests" ` - --interval PT1H ` - --aggregation Total ` - --start-time $startTime ` - --end-time $endTime - -# Storage capacity -az monitor metrics list ` - --resource "" ` - --metric "UsedCapacity,BlobCount" ` - --interval PT1H ` - --aggregation Average ` - --start-time $startTime ` - --end-time $endTime -``` - -### Step 7: Generate Optimization Report - -Create a comprehensive cost optimization report in the `output/` folder: - -**Use the `create_file` tool** with path `output/costoptimizereport.md`: - -**Report Structure:** -```markdown -# Azure Cost Optimization Report -**Generated**: - -## Executive Summary -- Total Monthly Cost: $X (πŸ’° ACTUAL DATA) -- Top Cost Drivers: [List top 3 resources with Azure Portal links] - -## Cost Breakdown -[Table with top 10 resources by cost, including Azure Portal links] - -## Free Tier Analysis -[Resources operating within free tiers showing $0 cost] - -## Orphaned Resources (Immediate Savings) -[From azqr - resources that can be deleted immediately] -- Resource name with Portal link - $X/month savings - -## Optimization Recommendations - -### Priority 1: High Impact, Low Risk -[Example: Delete orphaned resources] -- πŸ’° ACTUAL cost: $X/month -- πŸ“Š ESTIMATED savings: $Y/month -- Commands to execute (with warnings) - -### Priority 2: Medium Impact, Medium Risk -[Example: Rightsize VM from D4s_v5 to D2s_v5] -- πŸ’° ACTUAL baseline: D4s_v5, $X/month -- πŸ“ˆ ACTUAL metrics: CPU 8%, Memory 30% -- πŸ’΅ VALIDATED pricing: D4s_v5 $Y/hr, D2s_v5 $Z/hr -- πŸ“Š ESTIMATED savings: $S/month -- Commands to execute - -### Priority 3: Long-term Optimization -[Example: Reserved Instances, Storage tiering] - -## Total Estimated Savings -- Monthly: $X -- Annual: $Y - -## Implementation Commands -[Safe commands with approval warnings] - -## Validation Appendix - -### Data Sources and Files -- **Cost Query Results**: `output/cost-query-result.json` - - Raw cost data from Azure Cost Management API - - Audit trail proving actual costs at report generation time - - Keep for at least 12 months for historical comparison - - Contains every resource's exact cost over the analysis period -- **Pricing Sources**: [Links to Azure pricing pages] -- **Free Tier Allowances**: [Applicable allowances] - -> **Note**: The `temp/cost-query.json` file (if present) is a temporary query template and can be safely deleted. All permanent audit data is in the `output/` folder. -``` - -**Portal Link Format:** -``` -https://portal.azure.com/#@/resource/subscriptions//resourceGroups//providers////overview -``` - -### Step 8: Save Audit Trail - -Save all cost query results for validation: - -**Use the `create_file` tool** with path `output/cost-query-result.json`: - -```json -{ - "timestamp": "", - "subscription": "", - "resourceGroup": "", - "queries": [ - { - "queryType": "ActualCost", - "timeframe": "MonthToDate", - "query": { }, - "response": { } - } - ] -} -``` - -### Step 9: Clean Up Temporary Files - -Remove temporary query files and folder after the report is generated: - -```powershell -# Delete entire temp folder (no longer needed) -Remove-Item -Path "temp" -Recurse -Force -ErrorAction SilentlyContinue -``` - -> **Note**: The `temp/cost-query.json` file is only needed during API execution. The actual query and results are preserved in `output/cost-query-result*.json` for audit purposes. - -## Output - -The skill generates: -1. **Cost Optimization Report** (`output/costoptimizereport.md`) - - Executive summary with total costs and top drivers - - Detailed cost breakdown with Azure Portal links - - Prioritized recommendations with actual data and estimated savings - - Implementation commands with safety warnings - -2. **Cost Query Results** (`output/cost-query-result.json`) - - Audit trail of all cost queries and responses - - Validation evidence for recommendations - -## Important Notes - -### Data Classification -- πŸ’° **ACTUAL DATA** = Retrieved from Azure Cost Management API -- πŸ“ˆ **ACTUAL METRICS** = Retrieved from Azure Monitor -- πŸ’΅ **VALIDATED PRICING** = Retrieved from official Azure pricing pages -- πŸ“Š **ESTIMATED SAVINGS** = Calculated based on actual data and validated pricing - -### Best Practices -- Always query actual costs first - never estimate or assume -- Validate pricing from official sources - account for free tiers -- Use REST API for cost queries (more reliable than `az costmanagement query`) -- Save audit trail - include all queries and responses -- Include Azure Portal links for all resources -- Use UTF-8 encoding when creating report files -- For costs < $10/month, emphasize operational improvements over financial savings -- Never execute destructive operations without explicit approval - -### Common Pitfalls -- **Assuming costs**: Always query actual data from Cost Management API -- **Ignoring free tiers**: Many services have generous allowances (e.g., Container Apps: 180K vCPU-sec free/month) -- **Using wrong date ranges**: 30 days for costs, 14 days for utilization -- **Broken Portal links**: Verify tenant ID and resource ID format -- **Cost query failures**: Use `az rest` with JSON body, not `az costmanagement query` - -### Safety Requirements -- Get approval before deleting resources -- Test changes in non-production first -- Provide dry-run commands for validation -- Include rollback procedures -- Monitor impact after implementation - -## SDK Quick References - -- **Redis Management**: [.NET](references/sdk/azure-resource-manager-redis-dotnet.md) diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/templates/redis-detailed-cache-analysis.md b/.github/plugins/azure-skills/skills/azure-cost-optimization/templates/redis-detailed-cache-analysis.md deleted file mode 100644 index 8fd0ea79..00000000 --- a/.github/plugins/azure-skills/skills/azure-cost-optimization/templates/redis-detailed-cache-analysis.md +++ /dev/null @@ -1,96 +0,0 @@ -Redis Cost Optimization Report - Detailed Analysis -Subscription: Example-Subscription (12345678-1234-1234-1234-123456789abc) -Generated: January 26, 2026 - -═══════════════════════════════════════════════════════════════════ - -SUBSCRIPTION OVERVIEW -- Total Caches: 5 -- Current Monthly Cost: $850 -- Potential Savings: $425/month (50%) -- Critical Issues: 3 - -CRITICAL ISSUES (πŸ”΄ Immediate Action) - -[1] example-cache-001 - SKU: Premium P1 (6GB) - State: Failed - Location: eastus2 - Age: 12 days - Cost: $300/month - Tags: environment=dev, owner=user1@example.com - - ❌ Problem: Cache in Failed state for 12 days - πŸ’‘ Recommendation: Delete immediately - πŸ’° Savings: $300/month - - Action: az redis delete --name example-cache-001 --resource-group dev-rg - -[2] example-cache-002 - SKU: Premium P1 (6GB) - State: Running - Location: eastus2 - Age: 120 days - Cost: $300/month - Tags: environment=dev, owner=user2@example.com - - ⚠️ Problem: Premium tier in dev environment - πŸ’‘ Recommendation: Downgrade to Standard C3 (6GB) - πŸ’° Savings: $175/month - - Next Steps: - 1. Verify with owner: user2@example.com - 2. Schedule maintenance window - 3. az redis update --name example-cache-002 --resource-group dev-rg --sku Standard --vm-size C3 - -HIGH PRIORITY (🟠 Review This Week) - -[3] example-cache-003 - SKU: Standard C2 (2.5GB) - State: Running - Location: westus - Age: 180 days - Cost: $100/month - Tags: purpose=test, temporary=true, created=2025-07-15 - - ⚠️ Problem: Temporary test cache running for 6 months - πŸ’‘ Recommendation: Delete if no longer needed - πŸ’° Savings: $100/month - - Action: Confirm with team, then delete - -HEALTHY CACHES (🟒 No Action Needed) - -[4] example-cache-004 - SKU: Standard C3 (6GB) - State: Running - Cost: $125/month - Tags: environment=prod, owner=team@example.com - βœ“ Appropriate tier for production workload - -[5] example-cache-005 - SKU: Standard C1 (1GB) - State: Running - Cost: $75/month - Tags: environment=staging - βœ“ Cost-optimized for staging environment - -═══════════════════════════════════════════════════════════════════ - -SAVINGS SUMMARY -- Critical issues resolved: $425/month -- Total potential savings: $425/month (50% reduction) -- New monthly cost: $425/month - -RECOMMENDED ACTIONS -1. [Immediate] Confirm with the team, then delete example-cache-001 (Failed state) -2. [This Week] Downgrade example-cache-002 to Standard -3. [This Week] Confirm example-cache-003 still needed, delete if not - -Would you like me to: - A. Generate Azure CLI commands for these actions - B. Analyze another subscription - C. Export full report to CSV - D. Set up automated monitoring - -Please select (A/B/C/D): \ No newline at end of file diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/templates/redis-subscription-level-report.md b/.github/plugins/azure-skills/skills/azure-cost-optimization/templates/redis-subscription-level-report.md deleted file mode 100644 index 08e467df..00000000 --- a/.github/plugins/azure-skills/skills/azure-cost-optimization/templates/redis-subscription-level-report.md +++ /dev/null @@ -1,30 +0,0 @@ -Redis Cost Optimization Report -Tenant: Contoso Corp -Generated: January 26, 2026 -Subscriptions Analyzed: 3 (filtered by prefix "CacheTeam -") - -═══════════════════════════════════════════════════════════════════ - -EXECUTIVE SUMMARY -- Total Redis Caches: 20 -- Current Monthly Cost: $3,625 -- Potential Savings: $875/month (24.1%) -- Critical Issues: 4 caches requiring immediate action - -BY SUBSCRIPTION -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Subscription β”‚Cachesβ”‚ Cost/Mo β”‚ Savings/Mo β”‚ Priority β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ CacheTeam - Alpha β”‚ 5 β”‚ $850 β”‚ $425 β”‚ πŸ”΄ β”‚ -β”‚ CacheTeam - Beta β”‚ 3 β”‚ $375 β”‚ $0 β”‚ 🟒 β”‚ -β”‚ CacheTeam - Prod β”‚ 12 β”‚ $2,400 β”‚ $450 β”‚ 🟠 β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - -CRITICAL ISSUES (πŸ”΄ Immediate Action Required) -- CacheTeam - Alpha: 1 failed cache, 2 Premium in dev -- CacheTeam - Prod: 1 old test cache (180 days) - -Next Steps: -1. Review detailed analysis for CacheTeam - Alpha (type 'analyze alpha') -2. Review detailed analysis for CacheTeam - Prod (type 'analyze prod') -3. Generate full report with all recommendations (type 'full report') \ No newline at end of file diff --git a/.github/plugins/azure-skills/skills/azure-cost/SKILL.md b/.github/plugins/azure-skills/skills/azure-cost/SKILL.md new file mode 100644 index 00000000..0486c4e8 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/SKILL.md @@ -0,0 +1,138 @@ +--- +name: azure-cost +description: "Unified Azure cost management: query historical costs, forecast future spending, and optimize to reduce waste. WHEN: \"Azure costs\", \"Azure spending\", \"Azure bill\", \"cost breakdown\", \"cost by service\", \"cost by resource\", \"how much am I spending\", \"show my bill\", \"monthly cost summary\", \"cost trends\", \"top cost drivers\", \"actual cost\", \"amortized cost\", \"forecast spending\", \"projected costs\", \"estimate bill\", \"future costs\", \"budget forecast\", \"end of month costs\", \"how much will I spend\", \"optimize costs\", \"reduce spending\", \"find cost savings\", \"orphaned resources\", \"rightsize VMs\", \"cost analysis\", \"reduce waste\", \"unused resources\", \"optimize Redis costs\", \"cost by tag\", \"cost by resource group\", \"AKS cost analysis add-on\", \"namespace cost\", \"cost spike\", \"anomaly\", \"budget alert\", \"AKS cost visibility\". DO NOT USE FOR: deploying resources, provisioning infrastructure, diagnostics, security audits, or estimating costs for new resources not yet deployed." +license: MIT +metadata: + author: Microsoft + version: "1.0.0" +--- + +# Azure Cost Management Skill + +Unified skill for all Azure cost management tasks: querying historical costs, forecasting future spending, and optimizing to reduce waste. + +## When to Use This Skill + +Activate this skill when user wants to: +- Query or analyze Azure costs (how much am I spending, show my bill, cost breakdown) +- Break down costs by service, resource, location, or tag +- Analyze cost trends over time +- Forecast future Azure spending or project end-of-month costs +- Optimize Azure costs, reduce spending, or find savings +- Find orphaned or unused resources +- Rightsize Azure VMs, containers, or services +- Generate cost optimization reports + +## Quick Reference + +| Property | Value | +|----------|-------| +| **Query API Endpoint** | `POST {scope}/providers/Microsoft.CostManagement/query?api-version=2023-11-01` | +| **Forecast API Endpoint** | `POST {scope}/providers/Microsoft.CostManagement/forecast?api-version=2023-11-01` | +| **MCP Tools** | `azure__documentation`, `azure__extension_cli_generate`, `azure__get_azure_bestpractices` | +| **CLI** | `az rest`, `az monitor metrics list`, `az resource list` | +| **Required Role** | Cost Management Reader + Monitoring Reader + Reader on scope | + +## MCP Tools + +| Tool | Description | Parameters | When to Use | +|------|-------------|------------|-------------| +| `azure__documentation` | Search Azure documentation | `query` (Required): search terms | Research Cost Management API parameters and options | +| `azure__extension_cli_generate` | Generate Azure CLI commands | `intent` (Required): task description, `cli-type` (Required): `"az"` | Construct `az rest` commands for cost queries | +| `azure__get_azure_bestpractices` | Get Azure best practices | `intent` (Required): optimization context | Inform query design with cost management best practices | +| `azure__extension_azqr` | Run Azure Quick Review compliance scan | `subscription` (Required): subscription ID, `resource-group` (Optional): resource group name | Find orphaned resources and cost optimization opportunities | +| `azure__aks` | Azure Kubernetes Service operations | varies by sub-command | AKS cost analysis: list clusters, get node pools, inspect configuration | + +> πŸ’‘ **Tip:** Prefer MCP tools over direct CLI commands. Use `az rest` only when MCP tools don't cover the specific operation. + +--- + +## Routing + +Read the user's request and follow the appropriate workflow below. + +| User Intent | Workflow | Example Prompts | +|-------------|----------|-----------------| +| Understand current costs | [Cost Query Workflow](cost-query/workflow.md) | "how much am I spending", "cost by service", "show my bill" | +| Reduce costs / find waste | [Cost Optimization Workflow](cost-optimization/workflow.md) | "optimize costs", "find orphaned resources", "reduce spending" | +| Project future costs | [Cost Forecast Workflow](cost-forecast/workflow.md) | "forecast costs", "end of month estimate", "how much will I spend" | +| Full cost picture | All three workflows combined | "give me the full picture of my Azure costs" | + +> **Important:** When optimizing costs, always present the total bill and cost breakdown alongside optimization recommendations. + +--- + +## Scope Reference (Shared Across All Workflows) + +| Scope | URL Pattern | +|-------|-------------| +| Subscription | `/subscriptions/` | +| Resource Group | `/subscriptions//resourceGroups/` | +| Management Group | `/providers/Microsoft.Management/managementGroups/` | +| Billing Account | `/providers/Microsoft.Billing/billingAccounts/` | +| Billing Profile | `/providers/Microsoft.Billing/billingAccounts//billingProfiles/` | + +> πŸ’‘ **Tip:** These are scope paths only β€” not complete URLs. Combine with the API endpoint and version. + +--- + +## Part 1: Cost Query Workflow + +For the full cost query workflow (scope selection, report types, timeframes, dataset configuration, API calls, pagination, guardrails, examples, and error handling), see: + +πŸ“„ **[Cost Query Workflow](cost-query/workflow.md)** + +--- + +## Part 2: Cost Optimization Workflow + +For the full cost optimization workflow (prerequisites, best practices, Redis/AKS-specific analysis, Azure Quick Review, resource discovery, cost queries, pricing validation, utilization metrics, and report generation), see: + +πŸ“„ **[Cost Optimization Workflow](cost-optimization/workflow.md)** + +--- + +## Part 3: Cost Forecast Workflow + +For the full cost forecast workflow (scope selection, time period rules, dataset configuration, forecast-specific options, API calls, response interpretation, guardrails, and error handling), see: + +πŸ“„ **[Cost Forecast Workflow](cost-forecast/workflow.md)** + +--- + +## Data Classification + +- **ACTUAL DATA** = Retrieved from Azure Cost Management API +- **ACTUAL METRICS** = Retrieved from Azure Monitor +- **VALIDATED PRICING** = Retrieved from official Azure pricing pages +- **ESTIMATED SAVINGS** = Calculated based on actual data and validated pricing + +## Best Practices + +- Always query actual costs first β€” never estimate or assume +- Always present the total bill alongside optimization recommendations +- Validate pricing from official sources β€” account for free tiers +- Use REST API for cost queries (more reliable than `az costmanagement query`) +- Save audit trail β€” include all queries and responses +- Include Azure Portal links for all resources +- For costs < $10/month, emphasize operational improvements over financial savings +- Never execute destructive operations without explicit approval + +## Common Pitfalls + +- **Assuming costs**: Always query actual data from Cost Management API +- **Ignoring free tiers**: Many services have generous allowances +- **Using wrong date ranges**: 30 days for costs, 14 days for utilization +- **Not showing the bill**: Always present cost breakdown alongside optimization recommendations +- **Cost query failures**: Use `az rest` with JSON body, not `az costmanagement query` + +## Safety Requirements + +- Get approval before deleting resources +- Test changes in non-production first +- Provide dry-run commands for validation +- Include rollback procedures + +## SDK Quick References + +- **Redis Management**: [.NET](cost-optimization/sdk/azure-resource-manager-redis-dotnet.md) diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/error-handling.md b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/error-handling.md new file mode 100644 index 00000000..243d2912 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/error-handling.md @@ -0,0 +1,48 @@ +# Forecast API Error Handling + +## HTTP Status Codes + +| Status | Error | Cause | Remediation | +|---|---|---|---| +| 400 | Bad Request | Invalid request body, missing `dataset`, past-only dates, invalid field dependency combinations | Check request body structure; ensure `to` date is in the future; verify `includeActualCost`/`includeFreshPartialCost` dependency | +| 401 | Unauthorized | Authentication failure β€” missing or expired token | Re-authenticate with `az login` or refresh the access token | +| 403 | Forbidden | Insufficient permissions on the scope | Ensure the identity has **Cost Management Reader** role (or higher) on the target scope | +| 404 | Not Found | Invalid scope URL β€” subscription, resource group, or billing account not found | Verify the scope URL path and resource IDs are correct | +| 424 | Failed Dependency | Bad training data β€” forecast model cannot compute predictions | Falls back to actual costs if `includeActualCost=true`; otherwise suggest using **the Cost Query workflow (Part 1)** for historical data | +| 429 | Too Many Requests | Rate limited β€” QPU quota exceeded | Read `x-ms-ratelimit-microsoft.costmanagement-qpu-retry-after` header and wait before retrying | +| 503 | Service Unavailable | Temporary service issue | Check [Azure Status](https://status.azure.com) for service health. | + +## Validation Error Reference + +| Error Code | Description | Fix | +|---|---|---| +| `EmptyForecastRequestBody` | Request body is empty or null | Provide a complete request body with `type`, `timeframe`, `timePeriod`, and `dataset` | +| `InvalidForecastRequestBody` | Request body has invalid JSON structure | Check JSON syntax β€” verify braces, commas, and field names | +| `DontContainsDataSet` | The `dataset` field is missing from the request body | Add the `dataset` object with `granularity` and `aggregation` | +| `DontContainsValidTimeRangeWhileContainsPeriod` | `timePeriod` is present but `from` or `to` is invalid | Ensure both `from` and `to` are valid ISO 8601 datetime strings | +| `DontContainsValidTimeRangeWhileMonthlyAndIncludeCost` | Monthly granularity with `includeActualCost=true` but missing valid `timePeriod` | Add explicit `timePeriod` with valid `from` and `to` dates | +| `DontContainIncludeActualCostWhileIncludeFreshPartialCost` | `includeFreshPartialCost=true` without `includeActualCost=true` | Set `includeActualCost=true` or set `includeFreshPartialCost=false` | +| `CantForecastOnThePast` | Both `from` and `to` dates are in the past | Ensure the `to` date is in the future | + +## Forecast-Specific Scenarios + +| Scenario | Response | Action | +|---|---|---| +| "Forecast is unavailable for the specified time period" | Valid response with null/empty rows | Not an error β€” insufficient history (< 28 days). Suggest using **the Cost Query workflow (Part 1)** for available historical data. | +| "Can't forecast on the past" | 400 error with `CantForecastOnThePast` | Ensure the `to` date is in the future. | +| Bad training data | 424 Failed Dependency | If `includeActualCost=true`, the response falls back to actual cost data only. Otherwise, suggest using **the Cost Query workflow (Part 1)** for historical data. | +| Parsing exception | 400 Bad Request | Check JSON format β€” validate braces, quotes, commas, and field types. | + +## Retry Strategy + +| Status | Retry? | Strategy | +|---|---|---| +| 429 | βœ… Yes | Wait for duration specified in `x-ms-ratelimit-microsoft.costmanagement-qpu-retry-after` header. **Maximum 3 retries.** | +| 400 | ❌ No | Fix the request body based on the validation error code | +| 401 | ❌ No | Re-authenticate β€” the token is missing or expired | +| 403 | ❌ No | Grant **Cost Management Reader** role on the target scope | +| 404 | ❌ No | Fix the scope URL β€” verify subscription, resource group, or billing account IDs | +| 424 | ❌ No | Training data issue β€” retrying will not help. Fall back to actual costs or use **the Cost Query workflow (Part 1)** | +| 503 | ❌ No | Do not retry. Check [Azure Status](https://status.azure.com) for service health. | + +> ⚠️ **Warning:** Do not retry any errors except 429. All other errors indicate issues that must be fixed before re-attempting the request. diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/examples.md b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/examples.md new file mode 100644 index 00000000..4c153ade --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/examples.md @@ -0,0 +1,125 @@ +# Forecast API Examples + +Common forecast patterns with request bodies. Use the [SKILL.md workflow](../SKILL.md) to construct and execute the `az rest` command. + +## 1. Forecast Rest of Current Month (Daily) + +```json +{ + "type": "ActualCost", + "timeframe": "Custom", + "timePeriod": { + "from": "", + "to": "" + }, + "dataset": { + "granularity": "Daily", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "sorting": [ + { "direction": "Ascending", "name": "UsageDate" } + ] + }, + "includeActualCost": true, + "includeFreshPartialCost": true +} +``` + +> πŸ’‘ **Tip:** Set `from` to the first of the month β€” the response contains `Actual` rows up to today and `Forecast` rows for remaining days. + +--- + +## 2. Forecast Next 3 Months (Monthly) + +```json +{ + "type": "ActualCost", + "timeframe": "Custom", + "timePeriod": { + "from": "", + "to": "<3-months-out>" + }, + "dataset": { + "granularity": "Monthly", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "sorting": [ + { "direction": "Ascending", "name": "BillingMonth" } + ] + }, + "includeActualCost": true, + "includeFreshPartialCost": true +} +``` + +> πŸ’‘ **Tip:** Monthly granularity uses the `BillingMonth` column in the response. + +--- + +## 3. Forecast for Resource Group Scope + +```json +{ + "type": "ActualCost", + "timeframe": "Custom", + "timePeriod": { + "from": "", + "to": "" + }, + "dataset": { + "granularity": "Daily", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "sorting": [ + { "direction": "Ascending", "name": "UsageDate" } + ] + }, + "includeActualCost": true, + "includeFreshPartialCost": true +} +``` + +> πŸ’‘ **Tip:** Scope is set at the URL level. Use the resource group scope URL to limit the forecast. + +--- + +## 4. Forecast for Billing Account Scope + +```json +{ + "type": "ActualCost", + "timeframe": "Custom", + "timePeriod": { + "from": "", + "to": "" + }, + "dataset": { + "granularity": "Monthly", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "sorting": [ + { "direction": "Ascending", "name": "BillingMonth" } + ] + }, + "includeActualCost": true, + "includeFreshPartialCost": true +} +``` + +> πŸ’‘ **Tip:** Use URL pattern `/providers/Microsoft.Billing/billingAccounts//...`. Monthly granularity recommended for billing account forecasts. + +--- + +## Scope URL Reference + +| Scope | URL Pattern | +|---|---| +| Subscription | `/subscriptions//providers/Microsoft.CostManagement/forecast` | +| Resource Group | `/subscriptions//resourceGroups//providers/Microsoft.CostManagement/forecast` | +| Billing Account | `/providers/Microsoft.Billing/billingAccounts//providers/Microsoft.CostManagement/forecast` | + +> πŸ’‘ **Tip:** These are path-only patterns β€” not complete URLs. Append `?api-version=2023-11-01` when constructing the full request URL. diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/guardrails.md b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/guardrails.md new file mode 100644 index 00000000..561274db --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/guardrails.md @@ -0,0 +1,78 @@ +# Forecast API Guardrails + +Detailed guardrails derived from CCM-LUX getForecastData and CCM-UX-MIDDLEWARE Forecaster. + +## Time Period Validation + +| Rule | Detail | +|---|---| +| `to` date must be in the future | `numberOfDaysToForecast` must be > 0. Entirely past date ranges return a `CantForecastOnThePast` error. | +| `from` can be in the past | When `from` is in the past, the response includes actual costs from `from` to today and forecast costs from today to `to`. | +| Both dates must be valid | When `timePeriod` is present, both `from` and `to` must be valid parseable ISO 8601 datetime strings. | +| Monthly + includeActualCost | Monthly granularity with `includeActualCost=true` requires an explicit `timePeriod` with valid `from` and `to` dates. Omitting it produces `DontContainsValidTimeRangeWhileMonthlyAndIncludeCost`. | +| Maximum forecast period | 10 years maximum forecast window. | + +> ⚠️ **Warning:** If both `from` and `to` are in the past, the API returns `CantForecastOnThePast`. At least the `to` date must be in the future. + +## Training Data Requirements + +| Requirement | Value | +|---|---| +| Minimum historical data | 4 weeks (28 days) of cost data | +| Preferred training window | Up to 3 months of history | +| Late arrival tolerance | 2 days for billing data to arrive | +| New subscriptions (< 28 days) | Forecast unavailable | + +> ⚠️ **Warning:** New subscriptions with fewer than 28 days of cost history cannot generate forecasts. Suggest using **the Cost Query workflow (Part 1)** to retrieve available historical data instead. + +## Grouping Restriction + +| Aspect | Detail | +|---|---| +| Grouping support | ❌ **Not supported** | +| API limitation | This is a hard limitation of the Forecast API. The `grouping` field is not accepted in the request body. | +| Workaround | If the user requests a grouped forecast (e.g., forecast by resource group or service), inform them that grouping is not supported for forecasts. Suggest querying historical data with grouping using **the Cost Query workflow (Part 1)** instead. | + +> ⚠️ **Warning:** Even when using **the Cost Query workflow (Part 1)** for grouped historical data, `ResourceId` grouping is only supported at subscription scope and below. It is not supported at billing account, management group, or higher scopes. + +## Response Row Limit + +| Constraint | Detail | +|---|---| +| Maximum rows | 40 rows per forecast response | +| Daily example | 30 actual days + 30 forecast days = 60 rows β†’ **exceeds limit** | +| Recommendation | For daily granularity, keep forecast period to ~2–3 weeks | +| Longer periods | Use monthly granularity to stay within the row limit | + +> πŸ’‘ **Tip:** If the user needs a daily forecast for more than 2–3 weeks, consider splitting the request into smaller time windows or switching to monthly granularity. + +## includeActualCost / includeFreshPartialCost + +| Field | Default | Dependency | +|---|---|---| +| `includeActualCost` | `true` | None | +| `includeFreshPartialCost` | `true` | **Requires `includeActualCost=true`** | + +> ⚠️ **Warning:** Setting `includeFreshPartialCost=true` without `includeActualCost=true` produces validation error `DontContainIncludeActualCostWhileIncludeFreshPartialCost`. Always set both fields explicitly. + +## Forecast Availability + +The API returns "Forecast is unavailable for the specified time period" when: + +| Condition | Detail | +|---|---| +| Null/empty response rows | Response has no data rows | +| Insufficient training data | Scope has fewer than 28 days of cost history | +| No cost history | Scope has never had any cost data | + +> ⚠️ **Warning:** This is **not an error** β€” it is a valid response indicating the forecast model cannot generate predictions. Do not retry. Instead, suggest using **the Cost Query workflow (Part 1)** to retrieve whatever historical data is available. + +## Rate Limiting + +| Header | Description | +|---|---| +| `x-ms-ratelimit-microsoft.costmanagement-qpu-retry-after` | Seconds to wait before retrying (QPU-based) | +| `x-ms-ratelimit-microsoft.costmanagement-entity-retry-after` | Seconds to wait for entity-level throttle | +| `x-ms-ratelimit-microsoft.costmanagement-tenant-retry-after` | Seconds to wait for tenant-level throttle | + +The Forecast API uses the same QPU-based rate limiting as the Query API. When a 429 response is received, read the retry-after headers and wait before retrying. diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/request-body-schema.md b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/request-body-schema.md new file mode 100644 index 00000000..73386184 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/request-body-schema.md @@ -0,0 +1,111 @@ +# Forecast API Request Body Schema + +## Complete JSON Schema + +```json +{ + "type": "ActualCost", + "timeframe": "Custom", + "timePeriod": { + "from": "2024-01-01T00:00:00Z", + "to": "2024-03-31T00:00:00Z" + }, + "dataset": { + "granularity": "Daily", + "aggregation": { + "totalCost": { + "name": "Cost", + "function": "Sum" + } + }, + "sorting": [ + { + "direction": "Ascending", + "name": "UsageDate" + } + ], + "filter": { + "dimensions": { + "name": "ResourceGroupName", + "operator": "In", + "values": ["my-resource-group"] + } + } + }, + "includeActualCost": true, + "includeFreshPartialCost": true +} +``` + +## Field Reference + +| Field | Type | Required | Values | Description | +|---|---|---|---|---| +| `type` | string | βœ… | `ActualCost`, `AmortizedCost`, `Usage` | Cost type for the forecast | +| `timeframe` | string | βœ… | `Custom` | Must be `Custom` for forecast requests | +| `timePeriod` | object | βœ… | β€” | Start and end dates for the forecast window | +| `timePeriod.from` | string | βœ… | ISO 8601 datetime | Start date; can be in the past to include actuals | +| `timePeriod.to` | string | βœ… | ISO 8601 datetime | End date; **must be in the future** for forecast | +| `dataset` | object | βœ… | β€” | Dataset configuration for the forecast | +| `dataset.granularity` | string | βœ… | `Daily`, `Monthly` | Time granularity of forecast results | +| `dataset.aggregation` | object | βœ… | β€” | Aggregation functions to apply | +| `dataset.aggregation.totalCost.name` | string | βœ… | `Cost` | Column name to aggregate | +| `dataset.aggregation.totalCost.function` | string | βœ… | `Sum` | Aggregation function | +| `dataset.sorting` | array | Optional | β€” | Sort order for results | +| `dataset.sorting[].direction` | string | Optional | `Ascending`, `Descending` | Sort direction | +| `dataset.sorting[].name` | string | Optional | `UsageDate` | Column to sort by | +| `dataset.filter` | object | Optional | β€” | Filter expression (dimensions/tags) | +| `includeActualCost` | boolean | Optional | `true`, `false` | Include historical actual costs alongside forecast. Default: `true` | +| `includeFreshPartialCost` | boolean | Optional | `true`, `false` | Include partial cost data for recent days. Default: `true`. **Requires `includeActualCost=true`** | + +## Forecast-Specific Fields + +### `includeActualCost` + +- **Type:** boolean +- **Default:** `true` +- When `true`, the response includes historical actual cost rows from the `from` date up to today, alongside projected forecast rows from today to the `to` date. +- When `false`, only forecast (projected) rows are returned. + +### `includeFreshPartialCost` + +- **Type:** boolean +- **Default:** `true` +- When `true`, includes partial (incomplete) cost data for the most recent days where billing data is still arriving. +- ⚠️ **Requires `includeActualCost=true`.** Setting `includeFreshPartialCost=true` without `includeActualCost=true` produces a validation error (`DontContainIncludeActualCostWhileIncludeFreshPartialCost`). + +## Response Structure + +### Response Columns + +| Column | Type | Description | +|---|---|---| +| `Cost` | Number | The cost amount (actual or forecasted) | +| `UsageDate` / `BillingMonth` | Datetime | The date for the cost row | +| `CostStatus` | String | Indicates whether the row is historical or projected | +| `Currency` | String | Currency code (e.g., `USD`, `EUR`) | + +### CostStatus Values + +| Value | Meaning | +|---|---| +| `Actual` | Historical cost data (already incurred) | +| `Forecast` | Projected future cost (model prediction) | + +### Granularity and Date Column Mapping + +| Granularity | Date Column | +|---|---| +| `Daily` | `UsageDate` | +| `Monthly` | `BillingMonth` | + +## Key Differences from Query API Request Body + +| Aspect | Forecast API | Query API | +|---|---|---| +| Grouping | ❌ Not supported | βœ… Supported via `grouping` field | +| `timeframe` | Typically `Custom` only | Supports `Custom`, `MonthToDate`, `BillingMonthToDate`, etc. | +| `includeActualCost` | βœ… Forecast-specific field | ❌ Not applicable | +| `includeFreshPartialCost` | βœ… Forecast-specific field | ❌ Not applicable | +| Response `CostStatus` column | βœ… Distinguishes `Actual` vs `Forecast` rows | ❌ Not present | +| `to` date | Must be in the future | Can be any valid past/present date | diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/workflow.md b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/workflow.md new file mode 100644 index 00000000..7b30ba67 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-forecast/workflow.md @@ -0,0 +1,122 @@ +# Cost Forecast Workflow + +Use this workflow when the user wants to **project future costs**. + +> ⚠️ **Warning:** If the user wants **historical** cost data, use the [Cost Query Workflow](../cost-query/workflow.md). If they want to **reduce** costs, use the [Cost Optimization Workflow](../cost-optimization/workflow.md). + +## Key Differences from Query API + +| Aspect | Query API | Forecast API | +|--------|-----------|--------------| +| Purpose | Historical cost data | Projected future costs | +| Time period | Past dates only | Must include future dates | +| Grouping | Up to 2 dimensions | **Not supported** | +| `includeActualCost` | N/A | Include historical alongside forecast | +| Response columns | Cost, Date, Currency | Cost, Date, **CostStatus**, Currency | +| Max response rows | 5,000/page | 40 rows recommended | +| Timeframe | Multiple presets + Custom | Typically `Custom` only | + +## Step 1: Determine Scope + +Use the same scope patterns from the Scope Reference table in the main [SKILL.md](../SKILL.md#scope-reference-shared-across-all-workflows). + +## Step 2: Choose Report Type + +`ActualCost` is most common for forecasting. `AmortizedCost` for reservation/savings plan projections. + +## Step 3: Set Time Period + +> ⚠️ **Warning:** The `to` date **MUST** be in the future. + +- Set `timeframe` to `Custom` and provide `timePeriod` with `from` and `to` dates +- `from` can be in the past β€” shows actual costs up to today, then forecast to `to` +- Minimum 28 days of historical cost data required +- Maximum forecast period: 10 years + +> **Full rules:** [Forecast Guardrails](./guardrails.md) + +## Step 4: Configure Dataset + +- **Granularity**: `Daily` or `Monthly` recommended +- **Aggregation**: Typically `Sum` of `Cost` +- See [Forecast Request Body Schema](./request-body-schema.md) for full schema + +> ⚠️ **Warning:** Grouping is **NOT supported** for forecast. Suggest using the [Cost Query Workflow](../cost-query/workflow.md) for grouped historical data instead. + +## Step 5: Set Forecast-Specific Options + +| Field | Default | Description | +|-------|---------|-------------| +| `includeActualCost` | `true` | Include historical actual costs alongside forecast | +| `includeFreshPartialCost` | `true` | Include partial cost data for recent days. **Requires `includeActualCost: true`** | + +## Step 6: Construct and Execute + +**Create `temp/cost-forecast.json`:** +```json +{ + "type": "ActualCost", + "timeframe": "Custom", + "timePeriod": { + "from": "", + "to": "" + }, + "dataset": { + "granularity": "Daily", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "sorting": [{ "direction": "Ascending", "name": "UsageDate" }] + }, + "includeActualCost": true, + "includeFreshPartialCost": true +} +``` + +**Execute:** +```powershell +New-Item -ItemType Directory -Path "temp" -Force + +az rest --method post ` + --url "/subscriptions//providers/Microsoft.CostManagement/forecast?api-version=2023-11-01" ` + --body '@temp/cost-forecast.json' +``` + +## Step 7: Interpret Response + +| CostStatus | Meaning | +|------------|---------| +| `Actual` | Historical actual cost (when `includeActualCost: true`) | +| `Forecast` | Projected future cost | + +> πŸ’‘ **Tip:** "Forecast is unavailable for the specified time period" is not an error β€” it means the scope has insufficient historical data. Suggest using the [Cost Query Workflow](../cost-query/workflow.md) for available data. + +## Key Guardrails + +| Rule | Constraint | +|------|-----------| +| `to` date | Must be in the future | +| Grouping | Not supported | +| Min training data | 28 days of historical cost data | +| Max forecast period | 10 years | +| Response row limit | 40 rows recommended | +| `includeFreshPartialCost` | Requires `includeActualCost: true` | +| Monthly + includeActualCost | Requires explicit `timePeriod` | + +> **Full details:** [Forecast Guardrails](./guardrails.md) + +## Error Handling + +| Status | Error | Remediation | +|--------|-------|-------------| +| 400 | Can't forecast on the past | Ensure `to` date is in the future. | +| 400 | Missing dataset | Add required `dataset` field. | +| 400 | Invalid dependency | Set `includeActualCost: true` when using `includeFreshPartialCost`. | +| 403 | Forbidden | Needs **Cost Management Reader** role on scope. | +| 424 | Bad training data | Insufficient history; falls back to actual costs if available. | +| 429 | Rate limited | Retry after `x-ms-ratelimit-microsoft.costmanagement-qpu-retry-after` header. **Max 3 retries.** | +| 503 | Service unavailable | Check [Azure Status](https://status.azure.com). | + +> **Full details:** [Forecast Error Handling](./error-handling.md) + +For more forecast examples, see [forecast examples](./examples.md). diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/references/auth-best-practices.md b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/auth-best-practices.md similarity index 100% rename from .github/plugins/azure-skills/skills/azure-cost-optimization/references/auth-best-practices.md rename to .github/plugins/azure-skills/skills/azure-cost/cost-optimization/auth-best-practices.md diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-aks-anomalies.md b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/azure-aks-anomalies.md similarity index 100% rename from .github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-aks-anomalies.md rename to .github/plugins/azure-skills/skills/azure-cost/cost-optimization/azure-aks-anomalies.md diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-aks-cost-addon.md b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/azure-aks-cost-addon.md similarity index 100% rename from .github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-aks-cost-addon.md rename to .github/plugins/azure-skills/skills/azure-cost/cost-optimization/azure-aks-cost-addon.md diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-quick-review.md b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/azure-quick-review.md similarity index 100% rename from .github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-quick-review.md rename to .github/plugins/azure-skills/skills/azure-cost/cost-optimization/azure-quick-review.md diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-resource-graph.md b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/azure-resource-graph.md similarity index 95% rename from .github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-resource-graph.md rename to .github/plugins/azure-skills/skills/azure-cost/cost-optimization/azure-resource-graph.md index b86ad075..7b85c469 100644 --- a/.github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-resource-graph.md +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/azure-resource-graph.md @@ -1,102 +1,102 @@ -# Azure Resource Graph Queries for Cost Optimization - -Azure Resource Graph (ARG) enables fast, cross-subscription resource querying using KQL via `az graph query`. Use it to find orphaned resources, unused infrastructure, and optimization targets. - -## How to Query - -Use the `extension_cli_generate` MCP tool to generate `az graph query` commands: - -```yaml -mcp_azure_mcp_extension_cli_generate - intent: "query Azure Resource Graph to " - cli-type: "az" -``` - -Or construct directly: - -```bash -az graph query -q "" --query "data[].{name:name, type:type}" -o table -``` - -> ⚠️ **Prerequisite:** `az extension add --name resource-graph` - -## Key Tables - -| Table | Contains | -|-------|----------| -| `Resources` | All ARM resources (name, type, location, properties, tags) | -| `ResourceContainers` | Subscriptions, resource groups, management groups | -| `AdvisorResources` | Cost and performance recommendations | - -## Cost Optimization Query Patterns - -**Find orphaned (unattached) managed disks:** - -```kql -Resources -| where type =~ 'microsoft.compute/disks' -| where isempty(managedBy) -| project name, resourceGroup, location, diskSizeGb=properties.diskSizeGB, sku=sku.name -``` - -**Find unattached public IP addresses:** - -```kql -Resources -| where type =~ 'microsoft.network/publicipaddresses' -| where isempty(properties.ipConfiguration) -| project name, resourceGroup, location, sku=sku.name -``` - -**Find orphaned network interfaces:** - -```kql -Resources -| where type =~ 'microsoft.network/networkinterfaces' -| where isempty(properties.virtualMachine) -| project name, resourceGroup, location -``` - -**Resource count by SKU/tier (spot oversized resources):** - -```kql -Resources -| where isnotempty(sku.name) -| summarize count() by type, tostring(sku.name) -| order by count_ desc -``` - -**Tag coverage for cost allocation:** - -```kql -Resources -| extend hasCostCenter = isnotnull(tags['CostCenter']) -| summarize total=count(), tagged=countif(hasCostCenter) by type -| extend coverage=round(100.0 * tagged / total, 1) -| order by total desc -``` - -**Find idle load balancers (no backend pools):** - -```kql -Resources -| where type =~ 'microsoft.network/loadbalancers' -| where array_length(properties.backendAddressPools) == 0 -| project name, resourceGroup, location, sku=sku.name -``` - -**Get Advisor cost recommendations:** - -```kql -AdvisorResources -| where properties.category == 'Cost' -| project name, impact=properties.impact, description=properties.shortDescription.solution -``` - -## Tips - -- Use `=~` for case-insensitive type matching (resource types are lowercase) -- Navigate properties with `properties.fieldName` -- Use `--first N` to limit result count -- Use `--subscriptions` to scope to specific subscriptions -- Cross-reference orphaned resources with cost data from Cost Management API +# Azure Resource Graph Queries for Cost Optimization + +Azure Resource Graph (ARG) enables fast, cross-subscription resource querying using KQL via `az graph query`. Use it to find orphaned resources, unused infrastructure, and optimization targets. + +## How to Query + +Use the `extension_cli_generate` MCP tool to generate `az graph query` commands: + +```yaml +azure__extension_cli_generate + intent: "query Azure Resource Graph to " + cli-type: "az" +``` + +Or construct directly: + +```bash +az graph query -q "" --query "data[].{name:name, type:type}" -o table +``` + +> ⚠️ **Prerequisite:** `az extension add --name resource-graph` + +## Key Tables + +| Table | Contains | +|-------|----------| +| `Resources` | All ARM resources (name, type, location, properties, tags) | +| `ResourceContainers` | Subscriptions, resource groups, management groups | +| `AdvisorResources` | Cost and performance recommendations | + +## Cost Optimization Query Patterns + +**Find orphaned (unattached) managed disks:** + +```kql +Resources +| where type =~ 'microsoft.compute/disks' +| where isempty(managedBy) +| project name, resourceGroup, location, diskSizeGb=properties.diskSizeGB, sku=sku.name +``` + +**Find unattached public IP addresses:** + +```kql +Resources +| where type =~ 'microsoft.network/publicipaddresses' +| where isempty(properties.ipConfiguration) +| project name, resourceGroup, location, sku=sku.name +``` + +**Find orphaned network interfaces:** + +```kql +Resources +| where type =~ 'microsoft.network/networkinterfaces' +| where isempty(properties.virtualMachine) +| project name, resourceGroup, location +``` + +**Resource count by SKU/tier (spot oversized resources):** + +```kql +Resources +| where isnotempty(sku.name) +| summarize count() by type, tostring(sku.name) +| order by count_ desc +``` + +**Tag coverage for cost allocation:** + +```kql +Resources +| extend hasCostCenter = isnotnull(tags['CostCenter']) +| summarize total=count(), tagged=countif(hasCostCenter) by type +| extend coverage=round(100.0 * tagged / total, 1) +| order by total desc +``` + +**Find idle load balancers (no backend pools):** + +```kql +Resources +| where type =~ 'microsoft.network/loadbalancers' +| where array_length(properties.backendAddressPools) == 0 +| project name, resourceGroup, location, sku=sku.name +``` + +**Get Advisor cost recommendations:** + +```kql +AdvisorResources +| where properties.category == 'Cost' +| project name, impact=properties.impact, description=properties.shortDescription.solution +``` + +## Tips + +- Use `=~` for case-insensitive type matching (resource types are lowercase) +- Navigate properties with `properties.fieldName` +- Use `--first N` to limit result count +- Use `--subscriptions` to scope to specific subscriptions +- Cross-reference orphaned resources with cost data from Cost Management API diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/report-template.md b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/report-template.md new file mode 100644 index 00000000..6bd69d80 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/report-template.md @@ -0,0 +1,69 @@ +# Cost Optimization Report Template + +Use `create_file` with path `output/costoptimizereport.md` and the following structure: + +```markdown +# Azure Cost Optimization Report +**Generated**: + +## Executive Summary +- Total Monthly Cost: $X (ACTUAL DATA from Cost Management API) +- Top Cost Drivers: [List top 3 services with costs] +- Potential Savings: $Y/month + +## Cost Breakdown by Service +| Service | Cost (USD) | % of Total | +|---------|-----------|------------| +| ... | ... | ... | +| **Total** | **$X** | **100%** | + +## Free Tier Analysis +[Resources operating within free tiers] + +## Orphaned Resources (Immediate Savings) +[From azqr β€” resources that can be deleted immediately] + +## Optimization Recommendations + +### Priority 1: High Impact, Low Risk +- ACTUAL cost: $X/month +- ESTIMATED savings: $Y/month +- Commands to execute + +### Priority 2: Medium Impact, Medium Risk +- ACTUAL baseline, ACTUAL metrics, VALIDATED pricing +- ESTIMATED savings with commands + +### Priority 3: Long-term Optimization +[Reserved Instances, Storage tiering] + +## Total Estimated Savings +- Monthly: $X | Annual: $Y + +## Implementation Commands +[Safe commands with approval warnings] + +## Validation Appendix +- Cost Query Results: `output/cost-query-result.json` +- Pricing Sources: [Links] +``` + +## Portal Link Format + +Include Azure Portal links for all resources using this format: + +```text +https://portal.azure.com/#@/resource/subscriptions//resourceGroups//providers////overview +``` + +## Audit Trail + +Save cost query results to `output/cost-query-result.json` for reproducibility. + +## Cleanup + +After generating the report, remove temporary files: + +```powershell +Remove-Item -Path "temp" -Recurse -Force -ErrorAction SilentlyContinue +``` diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/references/sdk/azure-resource-manager-redis-dotnet.md b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/sdk/azure-resource-manager-redis-dotnet.md similarity index 100% rename from .github/plugins/azure-skills/skills/azure-cost-optimization/references/sdk/azure-resource-manager-redis-dotnet.md rename to .github/plugins/azure-skills/skills/azure-cost/cost-optimization/sdk/azure-resource-manager-redis-dotnet.md diff --git a/.github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-redis.md b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/services/redis/azure-cache-for-redis.md similarity index 84% rename from .github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-redis.md rename to .github/plugins/azure-skills/skills/azure-cost/cost-optimization/services/redis/azure-cache-for-redis.md index 0c47c9ac..05db2a64 100644 --- a/.github/plugins/azure-skills/skills/azure-cost-optimization/references/azure-redis.md +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/services/redis/azure-cache-for-redis.md @@ -33,16 +33,14 @@ When analyzing each cache, apply these prioritized rules: ## Report Templates ### Subscription-Level Summary -Quick overview of costs and issues per subscription (use for multi-subscription scans). -See [redis-subscription-level-report.md](../templates/redis-subscription-level-report.md) for template format. +Quick overview of costs and issues per subscription (use for multi-subscription scans). Include: subscription name/ID, total monthly cost, number of caches, cache count by SKU tier, and top issues found. ### Detailed Cache Analysis -Individual cache breakdown with specific recommendations. -See [redis-detailed-cache-analysis.md](../templates/redis-detailed-cache-analysis.md) for template format. +Individual cache breakdown with specific recommendations. Include: cache name, resource group, SKU tier, current cost, memory usage %, CPU usage %, connection count, and specific rightsizing recommendations. ## Tools & Commands -**MCP Tool:** `mcp_azure_mcp_redis` with command `redis_list` (parameter: `subscription`) +**MCP Tool:** `azure__redis` with command `redis_list` (parameter: `subscription`) **Azure CLI Equivalents:** - `az account list` - List subscriptions diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/workflow.md b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/workflow.md new file mode 100644 index 00000000..8f1721f4 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-optimization/workflow.md @@ -0,0 +1,188 @@ +# Cost Optimization Workflow + +Use this workflow when the user wants to **reduce their costs** β€” find waste, orphaned resources, rightsizing opportunities. + +> **Important:** Always present the total bill and cost breakdown (from the [Cost Query Workflow](../cost-query/workflow.md)) alongside optimization recommendations. + +## Step 0: Validate Prerequisites + +**Required Tools:** +- Azure CLI installed and authenticated (`az login`) +- Azure CLI extensions: `costmanagement`, `resource-graph` +- Azure Quick Review (azqr) installed β€” See [Azure Quick Review](./azure-quick-review.md) + +**Required Permissions:** +- Cost Management Reader role +- Monitoring Reader role +- Reader role on subscription/resource group + +**Verification commands:** +```powershell +az --version +az account show +az extension show --name costmanagement +azqr version +``` + +## Step 1: Load Best Practices + +```javascript +azure__get_azure_bestpractices({ + intent: "Get cost optimization best practices", + command: "get_bestpractices", + parameters: { resource: "cost-optimization", action: "all" } +}) +``` + +## Step 1.5: Redis-Specific Analysis (Conditional) + +**If the user specifically requests Redis cost optimization**, use the specialized Redis reference: + +**Reference**: [Azure Redis Cost Optimization](./services/redis/azure-cache-for-redis.md) + +**When to use:** +- User mentions "Redis", "Azure Cache for Redis", or "Azure Managed Redis" +- Focus is on Redis resource optimization, not general subscription analysis + +> πŸ’‘ **Note:** For general subscription-wide optimization, continue with Step 2. For Redis-only analysis, follow the Redis reference document. + +## Step 1.6: Choose Analysis Scope (for Redis-specific analysis) + +**If performing Redis cost optimization**, ask the user to select: +1. **Specific Subscription ID** +2. **Subscription Name** +3. **Subscription Prefix** (e.g., "CacheTeam") +4. **All My Subscriptions** +5. **Tenant-wide** + +Wait for user response before proceeding. + +## Step 1.7: AKS-Specific Analysis (Conditional) + +**If the user specifically requests AKS cost optimization**, use the specialized AKS reference files: + +**When to use AKS-specific analysis:** +- User mentions "AKS", "Kubernetes", "cluster", "node pool", "pod", or "kubectl" +- User wants to enable the AKS cost analysis add-on or namespace cost visibility +- User reports a cost spike, unusual cluster utilization, or wants budget alerts + +**Tool Selection:** +- **Prefer MCP first**: Use `azure__aks` for AKS operations (list clusters, get node pools, inspect configuration) β€” it provides richer metadata and is consistent with AKS skill conventions in this repo +- **Fall back to CLI**: Use `az aks` and `kubectl` only when the specific operation cannot be performed via the MCP surface + +**Reference files (load only what is needed for the request):** +- [Cost Analysis Add-on](./azure-aks-cost-addon.md) β€” enable namespace-level cost visibility +- [Anomaly Investigation](./azure-aks-anomalies.md) β€” cost spikes, scaling events, budget alerts + +> **Note**: For general subscription-wide cost optimization (including AKS resource groups), continue with Step 2. For AKS-focused analysis, follow the instructions in the relevant reference file above. + +## Step 1.8: Choose Analysis Scope (for AKS-specific analysis) + +**If performing AKS cost optimization**, ask the user to select their analysis scope: + +**Prompt the user with these options:** +1. **Specific Cluster Name** - Analyze a single AKS cluster +2. **Resource Group** - Analyze all clusters in a resource group +3. **Subscription ID** - Analyze all clusters in a subscription +4. **All My Clusters** - Scan all accessible clusters across subscriptions + +Wait for user response before proceeding to Step 2. + +## Step 2: Run Azure Quick Review + +Run azqr to find orphaned resources (immediate cost savings): + +**Reference**: [Azure Quick Review](./azure-quick-review.md) + +```yaml +azure__extension_azqr + subscription: "" + resource-group: "" # optional +``` + +**What to look for:** +- Orphaned resources: unattached disks, unused NICs, idle NAT gateways +- Over-provisioned resources: excessive retention periods, oversized SKUs +- Missing cost tags + +## Step 3: Discover Resources + +Use Azure Resource Graph for efficient cross-subscription resource discovery. See [Azure Resource Graph Queries](./azure-resource-graph.md) for orphaned resource detection patterns. + +```powershell +az account show +az resource list --subscription "" --resource-group "" +``` + +## Step 4: Query Actual Costs + +Get actual cost data from Azure Cost Management API (last 30 days). Use the [Cost Query Workflow](../cost-query/workflow.md) with this configuration: + +**Create `temp/cost-query.json`:** +```json +{ + "type": "ActualCost", + "timeframe": "Custom", + "timePeriod": { + "from": "", + "to": "" + }, + "dataset": { + "granularity": "None", + "aggregation": { + "totalCost": { + "name": "Cost", + "function": "Sum" + } + }, + "grouping": [ + { + "type": "Dimension", + "name": "ResourceId" + } + ] + } +} +``` + +> **Action Required**: Calculate `` (30 days ago) and `` (today) in ISO 8601 format. + +**Execute and save results to `output/cost-query-result.json`.** + +> πŸ’‘ **Tip:** Also run a cost-by-service query (grouping by `ServiceName`) to present the total bill breakdown alongside optimization recommendations. See [examples.md](../cost-query/examples.md). + +## Step 5: Validate Pricing + +Fetch current pricing from official Azure pricing pages using `fetch_webpage`: + +**Key services to validate:** +- Container Apps: https://azure.microsoft.com/pricing/details/container-apps/ +- Virtual Machines: https://azure.microsoft.com/pricing/details/virtual-machines/ +- App Service: https://azure.microsoft.com/pricing/details/app-service/ +- Log Analytics: https://azure.microsoft.com/pricing/details/monitor/ + +> **Important**: Check for free tier allowances β€” many Azure services have generous free limits. + +## Step 6: Collect Utilization Metrics + +Query Azure Monitor for utilization data (last 14 days) to support rightsizing recommendations: + +```powershell +$startTime = (Get-Date).AddDays(-14).ToString("yyyy-MM-ddTHH:mm:ssZ") +$endTime = Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ" + +# VM CPU utilization +az monitor metrics list ` + --resource "" ` + --metric "Percentage CPU" ` + --interval PT1H ` + --aggregation Average ` + --start-time $startTime ` + --end-time $endTime +``` + +## Step 7: Generate Optimization Report + +Generate a report to `output/costoptimizereport.md` that includes an executive summary, cost breakdown by service, free tier analysis, orphaned resources, prioritized optimization recommendations, and implementation commands. Save cost query results to `output/cost-query-result.json` for audit trail, then clean up temporary files. + +For the complete report template, see [report-template.md](./report-template.md). diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-query/dimensions-by-scope.md b/.github/plugins/azure-skills/skills/azure-cost/cost-query/dimensions-by-scope.md new file mode 100644 index 00000000..726ae024 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-query/dimensions-by-scope.md @@ -0,0 +1,182 @@ +# Dimensions by Scope + +Dimension availability matrix for the Cost Management Query API, organized by scope type and agreement type. + +## Scope URL Patterns + +| Scope | URL Pattern | +|-------|-------------| +| Subscription | `/subscriptions/` | +| Resource Group | `/subscriptions//resourceGroups/` | +| Management Group | `/providers/Microsoft.Management/managementGroups/` | +| Billing Account | `/providers/Microsoft.Billing/billingAccounts/` | +| Billing Profile | `/providers/Microsoft.Billing/billingAccounts//billingProfiles/` | +| Invoice Section | `/providers/Microsoft.Billing/billingAccounts//billingProfiles//invoiceSections/` | +| Department (EA) | `/providers/Microsoft.Billing/billingAccounts//departments/` | +| Enrollment Account (EA) | `/providers/Microsoft.Billing/billingAccounts//enrollmentAccounts/` | +| Customer (Partner) | `/providers/Microsoft.Billing/billingAccounts//customers/` | + +## Common Dimensions + +The following dimensions are available across most scopes: + +> ⚠️ **Warning:** `ResourceId` grouping is **only supported at subscription and resource group scopes**. At higher scopes (billing account, management group, billing profile, etc.), use `ServiceName`, `SubscriptionName`, or another supported dimension instead. See [guardrails.md](guardrails.md) for the full scope restriction table. + +| Dimension | Description | +|-----------|-------------| +| `ResourceGroupName` | Resource group containing the resource. | +| `ResourceId` | Full Azure resource ID. | +| `ResourceLocation` | Azure region where the resource is deployed. | +| `ServiceName` | Azure service name (e.g., Virtual Machines, Storage). | +| `ServiceFamily` | Service family grouping (e.g., Compute, Storage, Networking). | +| `ServiceTier` | Service tier or SKU tier (e.g., Standard, Premium). | +| `MeterCategory` | Top-level meter classification. | +| `MeterSubCategory` | Meter sub-classification. | +| `Meter` | Specific meter name. | +| `ChargeType` | Type of charge (e.g., Usage, Purchase, Refund). | +| `PublisherType` | Publisher type (e.g., Azure, Marketplace, AWS). | +| `PricingModel` | Pricing model (e.g., OnDemand, Reservation, SavingsPlan, Spot). | +| `SubscriptionName` | Subscription display name. | +| `SubscriptionId` | Subscription GUID. | +| `TagKey` | Azure resource tag key (use with `TagKey` column type in grouping). | +| `Product` | Product name from the price sheet. | +| `BenefitName` | Reservation or savings plan name. | +| `BillingPeriod` | Billing period identifier. | + +## Scope-Specific Dimensions + +Additional dimensions available only at certain scopes: + +| Scope | Additional Dimensions | +|-------|-----------------------| +| Subscription | _(common dimensions only)_ | +| Resource Group | _(common dimensions only)_ | +| Management Group | `DepartmentName`, `EnrollmentAccountName` | +| Billing Account | `BillingProfileName`, `DepartmentName`, `EnrollmentAccountName`, `InvoiceSectionName`, `Customer` | +| Billing Profile | `InvoiceSectionName`, `Customer` | +| Invoice Section | _(common dimensions only)_ | +| Department (EA) | `EnrollmentAccountName` | +| Enrollment Account (EA) | _(common dimensions only)_ | +| Customer (Partner) | _(common dimensions only)_ | + +## Agreement Type Dimension Sets + +Available dimensions vary by agreement type. Only dimensions listed for your agreement type are valid in GroupBy and Filter expressions. + +### EA (Enterprise Agreement) + +| Dimension | Available | +|-----------|-----------| +| `ResourceGroupName` | βœ… | +| `ResourceId` | βœ… ⚠️ | +| `SubscriptionName` | βœ… | +| `Product` | βœ… | +| `ResourceLocation` | βœ… | +| `ServiceName` | βœ… | +| `ServiceFamily` | βœ… | +| `TagKey` | βœ… | +| `MeterSubCategory` | βœ… | +| `PublisherType` | βœ… | +| `PricingModel` | βœ… | +| `ChargeType` | βœ… | +| `ServiceTier` | βœ… | +| `BenefitName` | βœ… | +| `BillingProfileName` | βœ… | +| `DepartmentName` | βœ… | +| `EnrollmentAccountName` | βœ… | +| `BillingPeriod` | βœ… | + +### MCA (Microsoft Customer Agreement) + +| Dimension | Available | +|-----------|-----------| +| `ResourceGroupName` | βœ… | +| `ResourceId` | βœ… ⚠️ | +| `SubscriptionName` | βœ… | +| `Product` | βœ… | +| `ResourceLocation` | βœ… | +| `ServiceName` | βœ… | +| `ServiceFamily` | βœ… | +| `TagKey` | βœ… | +| `MeterSubCategory` | βœ… | +| `PublisherType` | βœ… | +| `PricingModel` | βœ… | +| `ChargeType` | βœ… | +| `ServiceTier` | βœ… | +| `BenefitName` | βœ… | +| `BillingProfileName` | βœ… | +| `InvoiceSectionName` | βœ… | + +### MOSP (Microsoft Online Services Program / Pay-As-You-Go) + +| Dimension | Available | +|-----------|-----------| +| `ResourceGroupName` | βœ… | +| `ResourceId` | βœ… ⚠️ | +| `SubscriptionName` | βœ… | +| `Product` | βœ… | +| `ResourceLocation` | βœ… | +| `ServiceName` | βœ… | +| `ServiceFamily` | βœ… | +| `TagKey` | βœ… | +| `MeterSubCategory` | βœ… | +| `PublisherType` | βœ… | +| `PricingModel` | βœ… | +| `ChargeType` | βœ… | +| `ServiceTier` | βœ… | +| `BenefitName` | βœ… | + +> ⚠️ `ResourceId` is available as a dimension but **only works in GroupBy at subscription and resource group scopes**. At billing account or management group scopes, use `ServiceName` or `SubscriptionName` instead. + +### Comparison Summary + +| Dimension | EA | MCA | MOSP | +|-----------|----|----|------| +| `ResourceGroupName` | βœ… | βœ… | βœ… | +| `ResourceId` | βœ… ⚠️ | βœ… ⚠️ | βœ… ⚠️ | +| `SubscriptionName` | βœ… | βœ… | βœ… | +| `Product` | βœ… | βœ… | βœ… | +| `ResourceLocation` | βœ… | βœ… | βœ… | +| `ServiceName` | βœ… | βœ… | βœ… | +| `ServiceFamily` | βœ… | βœ… | βœ… | +| `TagKey` | βœ… | βœ… | βœ… | +| `MeterSubCategory` | βœ… | βœ… | βœ… | +| `PublisherType` | βœ… | βœ… | βœ… | +| `PricingModel` | βœ… | βœ… | βœ… | +| `ChargeType` | βœ… | βœ… | βœ… | +| `ServiceTier` | βœ… | βœ… | βœ… | +| `BenefitName` | βœ… | βœ… | βœ… | +| `BillingProfileName` | βœ… | βœ… | ❌ | +| `DepartmentName` | βœ… | ❌ | ❌ | +| `EnrollmentAccountName` | βœ… | ❌ | ❌ | +| `InvoiceSectionName` | ❌ | βœ… | ❌ | +| `BillingPeriod` | βœ… | ❌ | ❌ | + +## Scope Resolution Priority + +When multiple scope identifiers are available in context, use the following priority order (highest first): + +| Priority | Scope | Notes | +|----------|-------|-------| +| 1 | Management Group | Broadest organizational scope. | +| 2 | Resource Group | Narrowest resource scope. | +| 3 | Subscription | Default scope for most queries. | +| 4 | Billing Profile + Invoice Section | MCA billing hierarchy. | +| 5 | Billing Profile | MCA billing scope. | +| 6 | Department | EA organizational unit. | +| 7 | Enrollment Account | EA enrollment scope. | +| 8 | Customer | Partner/CSP customer scope. | +| 9 | Invoice Section | Standalone invoice section. | +| 10 | Billing Account | Top-level billing scope. | + +> πŸ’‘ **Tip:** When a user provides both a subscription and a resource group, prefer the Resource Group scope (priority 2) over Subscription (priority 3) for more targeted results. + +## Required Context Variables + +The following context variables are needed to resolve scope and validate dimensions: + +| Variable | Description | Used For | +|----------|-------------|----------| +| `AgreementType` | The agreement type (`EA`, `MCA`, `MOSP`). | Determines valid dimension set. | +| `AccountType` | Account type within the agreement. | Refines scope-specific behavior. | +| `CallScopeId` | The fully qualified scope URL for the API call. | Builds the request URL path. | diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-query/error-handling.md b/.github/plugins/azure-skills/skills/azure-cost/cost-query/error-handling.md new file mode 100644 index 00000000..b63ad21c --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-query/error-handling.md @@ -0,0 +1,67 @@ +# Cost Query Error Handling + +Detailed error handling reference for the Cost Management Query API. + +## HTTP Status Codes + +| Status | Error | Cause | Remediation | +|--------|-------|-------|-------------| +| 400 | `BadRequest` | Invalid request body, unsupported dimension, date range exceeds limits, malformed filter expression. | Validate request body against [request-body-schema.md](request-body-schema.md). Check dimension compatibility in [dimensions-by-scope.md](dimensions-by-scope.md). | +| 401 | `Unauthorized` | Missing or expired authentication token. | Re-authenticate with `az login` or refresh the bearer token. | +| 403 | `Forbidden` | Insufficient permissions on the target scope. User lacks Cost Management Reader or equivalent role. | Assign `Cost Management Reader` or `Cost Management Contributor` role on the scope. | +| 404 | `NotFound` | Scope does not exist, subscription not found, or resource group does not exist. | Verify the scope URL. Confirm the subscription ID and resource group name are correct. | +| 429 | `TooManyRequests` | Rate limit exceeded. QPU, entity, or tenant throttling triggered. | Retry after the duration specified in the `x-ms-ratelimit-microsoft.costmanagement-qpu-retry-after` header. | +| 503 | `ServiceUnavailable` | Cost Management service is temporarily unavailable. | Check [Azure Status](https://status.azure.com) for service health. | + +## Common Error Scenarios + +| Error Message / Scenario | Cause | Remediation | +|--------------------------|-------|-------------| +| "Agreement type X does not support Y scope" | Scope type is incompatible with the account's agreement type. | Use a compatible scope. EA accounts cannot use Invoice Section scope; MOSP accounts cannot use Department scope. | +| "Dimension Z is not valid for scope" | The requested dimension is not available for the current scope and agreement type combination. | Check [dimensions-by-scope.md](dimensions-by-scope.md) for valid dimensions. | +| "SubscriptionName filter without SubscriptionId" | EA + Management Group scope: filtering by `SubscriptionName` without also filtering by `SubscriptionId`. | Add a `SubscriptionId` filter alongside the `SubscriptionName` filter. See [guardrails.md](guardrails.md). | +| Date range exceeds granularity limit | `Daily` range > 31 days or `Monthly`/`None` range > 12 months. | System auto-truncates `from` date. To avoid silent truncation, ensure range is within limits. | +| Date range exceeds absolute limit (37 months) | `from` to `to` spans more than 37 months. | Reduce the date range to 37 months or less. Split into multiple queries if needed. | +| "Request body is null or invalid" | Missing or malformed JSON in the request body. | Validate JSON syntax. Ensure `type`, `timeframe`, and `dataset` fields are present. | +| Invalid filter structure | `And`/`Or` has fewer than 2 child expressions, or `Not` has more than 1. | Ensure `And`/`Or` contain 2+ children. Use `Not` with exactly 1 child. For single conditions, use the filter directly without a logical wrapper. | +| "The query usage is not supported for the scope" | The query type (e.g., `AmortizedCost`) is not supported at the given scope. | Try a different scope or query type. Not all scopes support all report types. | +| `BillingSubscriptionNotFound` | The subscription ID in the scope URL is invalid or not associated with the billing account. | Verify the subscription ID exists and is active. Check that it belongs to the expected billing account. | + +## Retry Strategy + +| Status | Retry? | Strategy | +|--------|--------|----------| +| 429 | βœ… Yes | Wait for the duration specified in the `x-ms-ratelimit-microsoft.costmanagement-qpu-retry-after` response header, then retry. **Maximum 3 retries.** | +| 400 | ❌ No | Fix the request. Review error message for specific field or validation issue. | +| 401 | ❌ No | Re-authenticate. Token has expired or is missing. | +| 403 | ❌ No | Fix permissions. Request appropriate RBAC role assignment on the scope. | +| 404 | ❌ No | Fix the scope URL. Verify resource exists. | +| 503 | ❌ No | Do not retry. Check [Azure Status](https://status.azure.com) for service health. | +| 5xx (other) | ❌ No | Do not retry. Investigate the error and check service health. | + +> ⚠️ **Warning:** Do not retry any errors except 429. All other errors indicate issues that must be fixed before re-attempting the request. + +## Error Response Structure + +All error responses follow a consistent JSON structure: + +```json +{ + "error": { + "code": "", + "message": "", + "details": [ + { + "code": "", + "message": "" + } + ] + } +} +``` + +| Field | Description | +|-------|-------------| +| `error.code` | Machine-readable error code (e.g., `BadRequest`, `BillingSubscriptionNotFound`). | +| `error.message` | Human-readable description of the error. | +| `error.details` | Optional array of additional detail objects with more specific error information. | diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-query/examples.md b/.github/plugins/azure-skills/skills/azure-cost/cost-query/examples.md new file mode 100644 index 00000000..03d96e37 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-query/examples.md @@ -0,0 +1,127 @@ +# Cost Management Query Examples + +Common query patterns with request bodies. Use the [SKILL.md workflow](../SKILL.md) to construct and execute the `az rest` command. + +## 1. Monthly Cost by Service + +```json +{ + "type": "ActualCost", + "timeframe": "MonthToDate", + "dataset": { + "granularity": "None", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "grouping": [ + { "type": "Dimension", "name": "ServiceName" } + ], + "sorting": [ + { "direction": "Descending", "name": "Cost" } + ] + } +} +``` + +--- + +## 2. Daily Cost Trend (Last 30 Days) + +```json +{ + "type": "ActualCost", + "timeframe": "Custom", + "timePeriod": { + "from": "2024-01-01T00:00:00Z", + "to": "2024-01-31T23:59:59Z" + }, + "dataset": { + "granularity": "Daily", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + } + } +} +``` + +> ⚠️ **Warning:** Daily granularity supports a maximum of 31 days. + +--- + +## 3. Cost by Resource Group with Tag Filter + +```json +{ + "type": "ActualCost", + "timeframe": "MonthToDate", + "dataset": { + "granularity": "None", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "grouping": [ + { "type": "Dimension", "name": "ResourceGroupName" } + ], + "filter": { + "Tags": { + "Name": "Environment", + "Operator": "In", + "Values": ["production", "staging"] + } + }, + "sorting": [ + { "direction": "Descending", "name": "Cost" } + ] + } +} +``` + +--- + +## 4. Amortized Cost for Reservation Analysis + +```json +{ + "type": "AmortizedCost", + "timeframe": "TheLastMonth", + "dataset": { + "granularity": "None", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "grouping": [ + { "type": "Dimension", "name": "BenefitName" } + ], + "sorting": [ + { "direction": "Descending", "name": "Cost" } + ] + } +} +``` + +> πŸ’‘ **Tip:** `AmortizedCost` spreads reservation purchases across the term for accurate daily/monthly effective cost. + +--- + +## 5. Top 10 Most Expensive Resources + +```json +{ + "type": "ActualCost", + "timeframe": "MonthToDate", + "dataset": { + "granularity": "None", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "grouping": [ + { "type": "Dimension", "name": "ResourceId" } + ], + "sorting": [ + { "direction": "Descending", "name": "Cost" } + ] + } +} +``` + +> πŸ’‘ **Tip:** Append `&$top=10` to the URL to limit results: `...query?api-version=2023-11-01&$top=10` diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-query/guardrails.md b/.github/plugins/azure-skills/skills/azure-cost/cost-query/guardrails.md new file mode 100644 index 00000000..86706880 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-query/guardrails.md @@ -0,0 +1,164 @@ +# Cost Query Guardrails + +Detailed validation rules and guardrails for the Cost Management Query API. The system applies these automatically, but understanding them helps avoid unexpected query modifications or errors. + +## Time Period Validation + +### Default Behavior + +| Scenario | System Behavior | +|----------|----------------| +| No time period specified | Defaults to current month start β†’ today. | +| `from` is after `to` | System silently swaps `from` and `to`. | + +### Future Date Handling + +| Scenario | System Behavior | +|----------|----------------| +| Both `from` and `to` are in the future | Entire period shifted to the equivalent period last year. | +| Only `to` is in the future | `to` is adjusted to today's date. | + +> ⚠️ **Warning:** Future date shifting happens silently. The response data will cover the adjusted period, not the originally requested dates. + +### Granularity-Based Range Limits + +| Granularity | Maximum Range | Truncation Behavior | +|-------------|---------------|---------------------| +| `Daily` | 31 days | `from` truncated to `to - 1 month + 1 day`. | +| `Monthly` | 12 months | `from` truncated to `to - 12 months + 1 day`. | +| `None` | 12 months | `from` truncated to `to - 12 months + 1 day`. | + +> ⚠️ **Warning:** The absolute API limit is **37 months**. Requests exceeding 37 months return HTTP 400 regardless of granularity. + +### Minimum Start Date + +| Constraint | Value | +|------------|-------| +| Earliest allowed `from` date | May 1, 2014 | + +### GroupBy Interaction with Time Period + +| Combination | System Behavior | +|-------------|----------------| +| GroupBy + Daily granularity | Time period adjusted to the last day of the requested range. | +| GroupBy + Monthly granularity | Time period adjusted to the last month of the requested range. | + +> πŸ’‘ **Tip:** When using GroupBy with Daily granularity over a multi-day range, the system may return data only for the last day. For full daily breakdown with grouping, ensure the range is within the 31-day limit. + +## ResourceId Scope Restriction + +> ⚠️ **Warning:** Grouping by `ResourceId` is **only supported at subscription scope and below** (subscription, resource group). It is NOT supported at higher scopes. + +| Scope | ResourceId GroupBy | +|-------|--------------------| +| Subscription | βœ… Supported | +| Resource Group | βœ… Supported | +| Billing Account | ❌ Not supported | +| Management Group | ❌ Not supported | +| Billing Profile | ❌ Not supported | +| Department (EA) | ❌ Not supported | +| Enrollment Account (EA) | ❌ Not supported | +| Invoice Section (MCA) | ❌ Not supported | +| Customer (Partner) | ❌ Not supported | + +When the user requests a cost breakdown by resource at a billing account or management group scope, use `ServiceName`, `SubscriptionName`, or another supported dimension instead. If per-resource detail is needed, narrow the scope to a specific subscription first. + +## Dataset Validation + +### GroupBy Constraints + +| Rule | Limit | Error Behavior | +|------|-------|----------------| +| Maximum GroupBy dimensions | 2 | Validation error if more than 2 specified. | +| Duplicate columns in GroupBy | Not allowed | Validation error on duplicate column names. | +| Same column in Aggregation and GroupBy | Not allowed | Validation error if a column appears in both. | + +### Aggregation Constraints + +| Rule | Details | +|------|---------| +| Standard queries aggregation function | Only `Sum` is allowed. | +| `Date` in aggregation with granularity | Not allowed. Cannot aggregate on `Date` when granularity is `Daily` or `Monthly`. | + +### Filter Constraints + +| Rule | Details | +|------|---------| +| `And` operator | Must have 2 or more child expressions. | +| `Or` operator | Must have 2 or more child expressions. | +| `Not` operator | Must have exactly 1 child expression. | + +> ⚠️ **Warning:** A filter with a single child in `And` or `Or` will fail validation. Wrap single-condition filters directly without a logical operator, or use `Not` for negation. + +## Scope & Dimension Compatibility + +Dimensions must be valid for the intersection of the agreement type **and** scope type. + +| Agreement Type | Unique Dimensions | Reference | +|----------------|-------------------|-----------| +| EA | `DepartmentName`, `EnrollmentAccountName`, `BillingPeriod` | See [dimensions-by-scope.md](dimensions-by-scope.md) | +| MCA | `InvoiceSectionName` | See [dimensions-by-scope.md](dimensions-by-scope.md) | +| MOSP | _(common dimensions only)_ | See [dimensions-by-scope.md](dimensions-by-scope.md) | + +| Validation | Error Behavior | +|------------|----------------| +| Dimension not valid for agreement type | `BillingSubscriptionNotFound` or dimension validation error. | +| Dimension not valid for scope type | `BadRequest` with invalid dimension message. | + +> ⚠️ **Warning:** Using an EA-only dimension (e.g., `DepartmentName`) on a MOSP subscription will return a validation error. Always verify the agreement type before selecting dimensions. + +## EA + Management Group Special Case + +| Scenario | Result | +|----------|--------| +| Filter by `SubscriptionName` without `SubscriptionId` at Management Group scope | Error returned. | +| Error message | _"To view cost data, the subscription ID is needed. Select Subscriptions to find the ID for your subscription, and then ask your question again."_ | + +**Remediation:** When filtering by subscription name at Management Group scope under EA, always include a `SubscriptionId` filter alongside the `SubscriptionName` filter. + +```json +{ + "And": [ + { + "Dimensions": { + "Name": "SubscriptionId", + "Operator": "In", + "Values": [""] + } + }, + { + "Dimensions": { + "Name": "SubscriptionName", + "Operator": "In", + "Values": ["My Subscription"] + } + } + ] +} +``` + +## Rate Limiting + +### QPU-Based Throttling + +| Tier | Description | +|------|-------------| +| Premium | Higher QPU allocation (EA, MCA enterprise). | +| Non-premium | Lower QPU allocation (MOSP, trial). | + +### Rate Limit Headers + +| Header | Description | +|--------|-------------| +| `x-ms-ratelimit-microsoft.costmanagement-qpu-retry-after` | Seconds to wait before retrying (QPU limit). | +| `x-ms-ratelimit-microsoft.costmanagement-entity-retry-after` | Seconds to wait before retrying (entity limit). | +| `x-ms-ratelimit-microsoft.costmanagement-tenant-retry-after` | Seconds to wait before retrying (tenant limit). | + +### Pagination + +| Parameter | Default | Maximum | +|-----------|---------|---------| +| Page size | 1,000 rows | 5,000 rows | +| Pagination | Use `nextLink` from response to fetch subsequent pages. | β€” | + +> πŸ’‘ **Tip:** For large result sets, always check the `nextLink` field in the response. If present, make additional requests to retrieve all pages. diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-query/request-body-schema.md b/.github/plugins/azure-skills/skills/azure-cost/cost-query/request-body-schema.md new file mode 100644 index 00000000..7b4a85a4 --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-query/request-body-schema.md @@ -0,0 +1,170 @@ +# Cost Management Query API β€” Request Body Schema + +Schema for the [Cost Management Query API](https://learn.microsoft.com/en-us/rest/api/cost-management/query/usage). + +## Request Body Structure + +```json +{ + "type": "", + "timeframe": "", + "timePeriod": { "from": "2024-01-01T00:00:00Z", "to": "2024-01-31T23:59:59Z" }, + "dataset": { + "granularity": "", + "aggregation": { "": { "name": "", "function": "" } }, + "grouping": [{ "type": "", "name": "" }], + "filter": { "" }, + "sorting": [{ "direction": "", "name": "" }] + } +} +``` + +## Top-Level Fields + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | string | Yes | `ActualCost`, `AmortizedCost`, or `Usage` | +| `timeframe` | string | Yes | Predefined or `Custom` time window | +| `timePeriod` | object | Conditional | Required when `timeframe` is `Custom`. Contains `from`/`to` ISO 8601 dates. | +| `dataset` | object | Yes | Defines granularity, aggregation, grouping, filtering, sorting | + +### Timeframe Values + +`WeekToDate` Β· `MonthToDate` Β· `BillingMonthToDate` Β· `YearToDate` Β· `TheLastWeek` Β· `TheLastMonth` Β· `TheLastBillingMonth` Β· `TheLastYear` Β· `TheLast7Days` Β· `TheLast3Months` Β· `Custom` + +## Dataset Fields + +### Granularity + +| Value | Max Range | Description | +|-------|-----------|-------------| +| `None` | 12 months | Aggregated total, no date breakdown | +| `Daily` | 31 days | Day-by-day breakdown | +| `Monthly` | 12 months | Month-by-month breakdown | + +### Aggregation + +```json +"aggregation": { "totalCost": { "name": "Cost", "function": "Sum" } } +``` + +| Field | Required | Description | +|-------|----------|-------------| +| `` (key) | Yes | Output column alias (e.g., `totalCost`) | +| `name` | Yes | Source column: `Cost`, `PreTaxCost`, or `UsageQuantity` | +| `function` | Yes | `Sum` (only supported function for cost queries) | + +### Grouping + +```json +"grouping": [ + { "type": "Dimension", "name": "ServiceName" }, + { "type": "TagKey", "name": "Environment" } +] +``` + +- `type`: `Dimension` (built-in) or `TagKey` (resource tag) +- Maximum **2** GroupBy dimensions per query. No duplicates. + +### Filter + +Filter expressions restrict which cost records are included. Filters support logical operators (`And`, `Or`, `Not`) and comparison operators on dimensions or tags. + +#### Filter Expression Structure + +```json +"filter": { + "And": [ + { + "Dimensions": { + "Name": "ResourceGroupName", + "Operator": "In", + "Values": ["rg-prod", "rg-staging"] + } + }, + { + "Not": { + "Tags": { + "Name": "Environment", + "Operator": "Equal", + "Values": ["dev"] + } + } + } + ] +} +``` + +#### Logical Operators + +| Operator | Description | Children | +|----------|-------------|----------| +| `And` | All child expressions must match. | 2 or more expressions. | +| `Or` | Any child expression must match. | 2 or more expressions. | +| `Not` | Negates a single child expression. | Exactly 1 expression. | + +> ⚠️ **Warning:** `And` and `Or` must contain at least 2 child expressions. `Not` must contain exactly 1. + +#### Comparison Operators (ComparisonOperator Enum) + +| Operator | Description | Example | +|----------|-------------|---------| +| `In` | Value is in the provided list. Supports multiple values. | `"Values": ["vm", "storage"]` | +| `Equal` | Exact match against a single value. | `"Values": ["production"]` | +| `Contains` | String contains the specified substring. | `"Values": ["prod"]` | +| `LessThan` | Numeric less-than comparison. | `"Values": ["100"]` | +| `GreaterThan` | Numeric greater-than comparison. | `"Values": ["0"]` | +| `NotEqual` | Value does not match the specified value. | `"Values": ["dev"]` | + +#### Filter Target Types + +| Target | Description | +|--------|-------------| +| `Dimensions` | Filter on built-in dimensions (e.g., `ResourceGroupName`, `ServiceName`). | +| `Tags` | Filter on Azure resource tags (e.g., `Environment`, `CostCenter`). | + +### Sorting + +```json +"sorting": [ + { "direction": "Descending", "name": "Cost" } +] +``` + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `direction` | string | Yes | `Ascending` or `Descending`. | +| `name` | string | Yes | Column name to sort by (must be present in aggregation or grouping). | + +## Response Structure + +```json +{ + "id": "", + "name": "", + "type": "Microsoft.CostManagement/query", + "properties": { + "nextLink": "", + "columns": [ + { "name": "Cost", "type": "Number" }, + { "name": "ServiceName", "type": "String" }, + { "name": "UsageDate", "type": "Number" }, + { "name": "Currency", "type": "String" } + ], + "rows": [ + [123.45, "Virtual Machines", 20240115, "USD"], + [67.89, "Storage", 20240115, "USD"] + ] + } +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `columns` | array | Array of column definitions with `name` and `type`. | +| `columns[].name` | string | Column name. | +| `columns[].type` | string | Data type: `Number` or `String`. | +| `rows` | array | Array of row arrays. Values ordered to match `columns`. | +| `nextLink` | string | URL for next page of results, or `null` if no more pages. | + +> πŸ’‘ **Tip:** `UsageDate` is returned as a number in `YYYYMMDD` format (e.g., `20240115`) when granularity is `Daily` or `Monthly`. diff --git a/.github/plugins/azure-skills/skills/azure-cost/cost-query/workflow.md b/.github/plugins/azure-skills/skills/azure-cost/cost-query/workflow.md new file mode 100644 index 00000000..c306b02b --- /dev/null +++ b/.github/plugins/azure-skills/skills/azure-cost/cost-query/workflow.md @@ -0,0 +1,139 @@ +# Cost Query Workflow + +Use this workflow when the user wants to **understand their costs** β€” breakdowns, trends, totals, top spenders. + +## Step 1: Determine Scope + +Identify the Azure scope for the cost query from the Scope Reference table in the main [SKILL.md](../SKILL.md#scope-reference-shared-across-all-workflows). + +## Step 2: Choose Report Type + +| Type | Description | +|------|-------------| +| `ActualCost` | Actual billed costs including purchases | +| `AmortizedCost` | Reservation/savings plan costs spread across usage period | +| `Usage` | Usage-based cost data | + +## Step 3: Set Timeframe + +Use a preset timeframe (e.g., `MonthToDate`, `TheLastMonth`, `TheLastYear`) or `Custom` with a `timePeriod` object. + +> ⚠️ **Warning:** Key time period guardrails: +> - **Daily granularity**: max **31 days** +> - **Monthly/None granularity**: max **12 months** +> - `Custom` timeframe **requires** a `timePeriod` object with `from` and `to` dates +> - Future dates in historical queries are silently adjusted (see guardrails for details) +> +> See [guardrails.md](./guardrails.md) for the complete set of validation rules. + +## Step 4: Configure Dataset + +Define granularity, aggregation, grouping, filtering, and sorting in the `dataset` object. + +- **Granularity**: `None`, `Daily`, or `Monthly` +- **Aggregation**: Use `Sum` on `Cost` or `PreTaxCost` for total cost +- **Grouping**: Up to **2** `GroupBy` dimensions (e.g., `ServiceName`, `ResourceGroupName`) +- **Filtering**: Use `Dimensions` or `Tags` filters with `Name`, `Operator` (`In`, `Equal`, `Contains`), and `Values` fields +- **Sorting**: Order results by cost or dimension columns + +> πŸ’‘ **Tip:** Not all dimensions are available at every scope. See [dimensions-by-scope.md](./dimensions-by-scope.md) for the availability matrix. + +For the full request body schema, see [request-body-schema.md](./request-body-schema.md). + +## Step 5: Construct and Execute the API Call + +Use `az rest` to call the Cost Management Query API. + +**Create cost query file:** + +Create `temp/cost-query.json` with: +```json +{ + "type": "ActualCost", + "timeframe": "MonthToDate", + "dataset": { + "granularity": "None", + "aggregation": { + "totalCost": { + "name": "Cost", + "function": "Sum" + } + }, + "grouping": [ + { + "type": "Dimension", + "name": "ServiceName" + } + ] + } +} +``` + +**Execute cost query:** +```powershell +# Create temp folder +New-Item -ItemType Directory -Path "temp" -Force + +# Query using REST API (more reliable than az costmanagement query) +az rest --method post ` + --url "/providers/Microsoft.CostManagement/query?api-version=2023-11-01" ` + --body '@temp/cost-query.json' +``` + +## Step 6: Handle Pagination and Errors + +- The API returns a maximum of **5,000 rows** per page (default: 1,000). +- If `nextLink` is present in the response, follow it to retrieve additional pages. +- Handle rate limiting (HTTP 429) by respecting `Retry-After` headers. + +See [error-handling.md](./error-handling.md) for the full error reference. + +## Key Guardrails + +| Rule | Constraint | +|------|-----------| +| Daily granularity max range | 31 days | +| Monthly/None granularity max range | 12 months | +| Absolute API max range | 37 months | +| Max GroupBy dimensions | 2 | +| ResourceId grouping scope | Subscription and resource group only β€” not supported at billing account, management group, or higher scopes | +| Max rows per page | 5,000 | +| Custom timeframe | Requires `timePeriod` with `from`/`to` | +| Filter AND/OR | Must have at least 2 expressions | + +## Examples + +**Cost by service for the current month:** + +```powershell +az rest --method post ` + --url "/subscriptions//providers/Microsoft.CostManagement/query?api-version=2023-11-01" ` + --body '{ + "type": "ActualCost", + "timeframe": "MonthToDate", + "dataset": { + "granularity": "None", + "aggregation": { + "totalCost": { "name": "Cost", "function": "Sum" } + }, + "grouping": [ + { "type": "Dimension", "name": "ServiceName" } + ] + } + }' +``` + +For more examples including daily trends, tag-based filtering, and multi-dimension queries, see [examples.md](./examples.md). + +## Error Handling + +| HTTP Status | Error | Remediation | +|-------------|-------|-------------| +| 400 | Invalid request body | Check schema, date ranges, and dimension compatibility. | +| 401 | Unauthorized | Verify authentication (`az login`). | +| 403 | Forbidden | Ensure Cost Management Reader role on scope. | +| 404 | Scope not found | Verify scope URL and resource IDs. | +| 429 | Too many requests | Retry after `x-ms-ratelimit-microsoft.costmanagement-qpu-retry-after` header. **Max 3 retries.** | +| 503 | Service unavailable | Check [Azure Status](https://status.azure.com). | + +See [error-handling.md](./error-handling.md) for detailed error handling including rate limit headers and retry strategies. diff --git a/.github/plugins/azure-skills/skills/azure-deploy/SKILL.md b/.github/plugins/azure-skills/skills/azure-deploy/SKILL.md index 25aee0e6..0817c43a 100644 --- a/.github/plugins/azure-skills/skills/azure-deploy/SKILL.md +++ b/.github/plugins/azure-skills/skills/azure-deploy/SKILL.md @@ -4,7 +4,7 @@ description: "Execute Azure deployments for ALREADY-PREPARED applications that h license: MIT metadata: author: Microsoft - version: "1.0.10" + version: "1.0.11" --- # Azure Deploy diff --git a/.github/plugins/azure-skills/skills/azure-deploy/references/recipes/azd/errors.md b/.github/plugins/azure-skills/skills/azure-deploy/references/recipes/azd/errors.md index 375e876b..097effe2 100644 --- a/.github/plugins/azure-skills/skills/azure-deploy/references/recipes/azd/errors.md +++ b/.github/plugins/azure-skills/skills/azure-deploy/references/recipes/azd/errors.md @@ -11,6 +11,7 @@ These errors occur **during** `azd up` execution: | Deploy failed | Build or Docker errors | Check build logs | | Package failed | Missing Dockerfile or deps | Verify Dockerfile exists and dependencies | | Quota exceeded | Subscription limits | Request increase or change region | +| `PrincipalId '...' has type 'ServicePrincipal', which is different from specified PrincipalType 'User'` | Base template RBAC assigns roles with `principalType: 'User'` but deploying identity is a service principal (CI/CD) | Set `allowUserIdentityPrincipal: false` in the `storageEndpointConfig` variable in `infra/main.bicep`. Do NOT try clearing `AZURE_PRINCIPAL_ID` β€” azd repopulates it. See [Principal Type Mismatch](#principal-type-mismatch). | | `could not determine container registry endpoint` | Missing `AZURE_CONTAINER_REGISTRY_ENDPOINT` | See [Missing Container Registry Variables](#missing-container-registry-variables) | | `map has no entry for key "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID"` | Missing managed identity env vars | See [Missing Container Registry Variables](#missing-container-registry-variables) | | `map has no entry for key "MANAGED_IDENTITY_CLIENT_ID"` | Missing managed identity client ID | See [Missing Container Registry Variables](#missing-container-registry-variables) | @@ -86,6 +87,32 @@ After fixing the issue: azd up --no-prompt ``` +## Principal Type Mismatch + +**Symptom:** `azd up` fails during provisioning with: + +``` +PrincipalId '...' has type 'ServicePrincipal', which is different from specified PrincipalType 'User' +``` + +**Cause:** Many AZD templates (e.g., `functions-quickstart-python-http-azd`) include RBAC role assignments for the deploying user with hardcoded `principalType: 'User'`. This is controlled by an `allowUserIdentityPrincipal` flag in `main.bicep`'s `storageEndpointConfig` variable. When deploying from CI/CD with a service principal, `azd` sets `AZURE_PRINCIPAL_ID` to that service principal's object ID, but the Bicep still tries to create a role assignment with `principalType: 'User'`, causing ARM to reject it. + +**Solution:** + +In `infra/main.bicep`, find the `storageEndpointConfig` variable and set `allowUserIdentityPrincipal` to `false`: + +```bicep +var storageEndpointConfig = { + enableBlob: true + enableQueue: false + enableTable: false + enableFiles: false + allowUserIdentityPrincipal: false // Set to false for service principal deployments +} +``` + +> ⚠️ **Do NOT** try to fix this by running `azd env set AZURE_PRINCIPAL_ID ""`. The `azd` CLI repopulates this value from the current auth context, so clearing it has no effect. + ## Cleanup (DESTRUCTIVE) ```bash diff --git a/.github/plugins/azure-skills/skills/azure-deploy/references/troubleshooting.md b/.github/plugins/azure-skills/skills/azure-deploy/references/troubleshooting.md index 4eeb715d..e5f4c24d 100644 --- a/.github/plugins/azure-skills/skills/azure-deploy/references/troubleshooting.md +++ b/.github/plugins/azure-skills/skills/azure-deploy/references/troubleshooting.md @@ -112,6 +112,8 @@ azd env set STORAGE_SKU 3. **If no mapping exists**, add one to `infra/main.parameters.json`: ```json { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", "parameters": { "storageAccountSku": { "value": "${STORAGE_SKU}" @@ -120,6 +122,8 @@ azd env set STORAGE_SKU } ``` +> ⚠️ **Warning:** `main.parameters.json` uses ARM JSON syntax. Do **not** use `.bicepparam` syntax (`using`, `param`, `readEnvironmentVariable()`) in this file β€” `azd` will fail with a JSON parse error. + Then ask the user for the desired value and set the environment variable: ```bash azd env set STORAGE_SKU diff --git a/.github/plugins/azure-skills/skills/azure-prepare/SKILL.md b/.github/plugins/azure-skills/skills/azure-prepare/SKILL.md index d66a3a2c..7cc968c5 100644 --- a/.github/plugins/azure-skills/skills/azure-prepare/SKILL.md +++ b/.github/plugins/azure-skills/skills/azure-prepare/SKILL.md @@ -4,7 +4,7 @@ description: "Prepare Azure apps for deployment (infra Bicep/Terraform, azure.ya license: MIT metadata: author: Microsoft - version: "1.1.1" + version: "1.1.2" --- # Azure Prepare @@ -66,7 +66,7 @@ Activate this skill when user wants to: | Azure Functions, function app, serverless function, timer trigger, HTTP trigger, func new | Stay in **azure-prepare** β€” prefer Azure Functions templates in Step 4 | | APIM, API Management, API gateway, deploy APIM | Stay in **azure-prepare** β€” see [APIM Deployment Guide](references/apim.md) | | AI gateway, AI gateway policy, AI gateway backend, AI gateway configuration | **azure-aigateway** | -| workflow, orchestration, multi-step, pipeline, fan-out/fan-in, saga, long-running process, durable | Stay in **azure-prepare** β€” select **durable** recipe in Step 4. **MUST** load [durable.md](references/services/functions/durable.md) and [DTS reference](references/services/durable-task-scheduler/README.md). Generate `Microsoft.DurableTask/schedulers` + `taskHubs` Bicep resources. | +| workflow, orchestration, multi-step, pipeline, fan-out/fan-in, saga, long-running process, durable, order processing | Stay in **azure-prepare** β€” select **durable** recipe in Step 4. **MUST** load [durable.md](references/services/functions/durable.md), [DTS reference](references/services/durable-task-scheduler/README.md), and [DTS Bicep patterns](references/services/durable-task-scheduler/bicep.md). | > ⚠️ Check the user's **prompt text** β€” not just existing code. Critical for greenfield projects with no codebase to scan. See [full routing table](references/specialized-routing.md). diff --git a/.github/plugins/azure-skills/skills/azure-prepare/references/recipes/azd/iac-rules.md b/.github/plugins/azure-skills/skills/azure-prepare/references/recipes/azd/iac-rules.md index 0497e57f..9d78fe55 100644 --- a/.github/plugins/azure-skills/skills/azure-prepare/references/recipes/azd/iac-rules.md +++ b/.github/plugins/azure-skills/skills/azure-prepare/references/recipes/azd/iac-rules.md @@ -39,9 +39,26 @@ Before finalizing generated guidance: |-------------|---------| | Location | `./infra/` folder | | Entry point | `main.bicep` with `targetScope = 'subscription'` | -| Parameters | `main.parameters.json` | +| Parameters | `main.parameters.json` (ARM JSON β€” see format below) | | Modules | `./infra/modules/*.bicep` with `targetScope = 'resourceGroup'` | +## Parameter File Format + +`main.parameters.json` uses ARM JSON syntax. Do **not** use `.bicepparam` syntax (`using`, `param`, `readEnvironmentVariable()`) in this file β€” `azd` will fail with a JSON parse error. + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { "value": "${AZURE_ENV_NAME}" }, + "location": { "value": "${AZURE_LOCATION}" } + } +} +``` + +Use `azd env set` to supply values. During `azd provision`, azd substitutes `${VAR}` placeholders with values from the environment. + ## Naming Convention > ⚠️ **Before generating any resource name in Bicep, check [Resource naming rules](https://learn.microsoft.com/azure/azure-resource-manager/management/resource-name-rules) for that resource type's valid characters, length limits, and uniqueness scope.** Some resources forbid dashes or special characters, require globally unique names, or have short length limits. Adapt the pattern below accordingly. diff --git a/.github/plugins/azure-skills/skills/azure-prepare/references/recipes/bicep/patterns.md b/.github/plugins/azure-skills/skills/azure-prepare/references/recipes/bicep/patterns.md index 3892bad0..c18d8df2 100644 --- a/.github/plugins/azure-skills/skills/azure-prepare/references/recipes/bicep/patterns.md +++ b/.github/plugins/azure-skills/skills/azure-prepare/references/recipes/bicep/patterns.md @@ -49,17 +49,26 @@ output resourceGroupName string = rg.name ## main.parameters.json +> ⚠️ **Warning:** This file uses ARM JSON syntax. Do **not** use `.bicepparam` syntax (`using`, `param`, `readEnvironmentVariable()`) in this file β€” `azd` will fail with a JSON parse error. + ```json { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { - "environmentName": { "value": "dev" }, - "location": { "value": "eastus2" } + "environmentName": { "value": "${AZURE_ENV_NAME}" }, + "location": { "value": "${AZURE_LOCATION}" } } } ``` +Use `azd env set` to supply values at deploy time: + +```bash +azd env set AZURE_ENV_NAME myapp-1234 +azd env set AZURE_LOCATION eastus2 +``` + ## Naming Convention ```bicep diff --git a/.github/plugins/azure-skills/skills/azure-prepare/references/research.md b/.github/plugins/azure-skills/skills/azure-prepare/references/research.md index 24a2284f..f9c8baf1 100644 --- a/.github/plugins/azure-skills/skills/azure-prepare/references/research.md +++ b/.github/plugins/azure-skills/skills/azure-prepare/references/research.md @@ -83,7 +83,7 @@ Invoke related skills for specialized scenarios: | Need detailed security hardening | `azure-security-hardening` | | Setting up App Insights instrumentation | `appinsights-instrumentation` | | Building AI applications | `microsoft-foundry` | -| Cost-sensitive deployment | `azure-cost-optimization` | +| Cost-sensitive deployment | `azure-cost` | **Skill/Reference Invocation Pattern:** diff --git a/.github/plugins/azure-skills/skills/azure-prepare/references/specialized-routing.md b/.github/plugins/azure-skills/skills/azure-prepare/references/specialized-routing.md index 5f284781..10c6d311 100644 --- a/.github/plugins/azure-skills/skills/azure-prepare/references/specialized-routing.md +++ b/.github/plugins/azure-skills/skills/azure-prepare/references/specialized-routing.md @@ -11,7 +11,7 @@ | **1 (highest)** | Lambda, AWS Lambda, migrate AWS, migrate GCP, Lambda to Functions, migrate from AWS, migrate from GCP | **azure-cloud-migrate** | Phase 1 Step 4 (Select Recipe) β€” azure-cloud-migrate does assessment + code conversion, then azure-prepare takes over for infrastructure, local testing, or deployment | | 2 | copilot SDK, copilot app, copilot-powered, @github/copilot-sdk, CopilotClient, sendAndWait, copilot-sdk-service | **azure-hosted-copilot-sdk** | Phase 1 Step 4 (Select Recipe) | | 3 | Azure Functions, function app, serverless function, timer trigger, HTTP trigger, queue trigger, func new, func start | Stay in **azure-prepare** | Phase 1 Step 4 (Select Recipe) β€” prefer Azure Functions templates | -| 4 (lowest) | workflow, orchestration, multi-step, pipeline, fan-out/fan-in, saga, long-running process, durable | Stay in **azure-prepare** | Phase 1 Step 4 β€” select **durable** recipe. **MUST** load [durable.md](services/functions/durable.md) and [DTS reference](services/durable-task-scheduler/README.md). Generate `Microsoft.DurableTask/schedulers` + `taskHubs` Bicep resources. | +| 4 (lowest) | workflow, orchestration, multi-step, pipeline, fan-out/fan-in, saga, long-running process, durable, order processing | Stay in **azure-prepare** | Phase 1 Step 4 β€” select **durable** recipe. **MUST** load [durable.md](services/functions/durable.md), [DTS reference](services/durable-task-scheduler/README.md), and [DTS Bicep patterns](services/durable-task-scheduler/bicep.md). | > ⚠️ This checks the user's **prompt text**, not just existing code. Essential for greenfield projects where there is no codebase to scan. diff --git a/.github/plugins/azure-skills/skills/azure-resource-lookup/SKILL.md b/.github/plugins/azure-skills/skills/azure-resource-lookup/SKILL.md index c9baea94..1fb773eb 100644 --- a/.github/plugins/azure-skills/skills/azure-resource-lookup/SKILL.md +++ b/.github/plugins/azure-skills/skills/azure-resource-lookup/SKILL.md @@ -1,6 +1,6 @@ --- name: azure-resource-lookup -description: "List, find, and show Azure resources across subscriptions or resource groups. Handles prompts like \"list websites\", \"list virtual machines\", \"list my VMs\", \"show storage accounts\", \"find container apps\", and \"what resources do I have\". USE FOR: resource inventory, find resources by tag, tag analysis, orphaned resource discovery (not for cost analysis), unattached disks, count resources by type, cross-subscription lookup, and Azure Resource Graph queries. DO NOT USE FOR: deploying/changing resources, cost optimization, or non-Azure clouds." +description: "List, find, and show Azure resources across subscriptions or resource groups. Handles prompts like \"list websites\", \"list virtual machines\", \"list my VMs\", \"show storage accounts\", \"find container apps\", and \"what resources do I have\". USE FOR: resource inventory, find resources by tag, tag analysis, orphaned resource discovery (not for cost analysis), unattached disks, count resources by type, cross-subscription lookup, and Azure Resource Graph queries. DO NOT USE FOR: deploying/changing resources (use azure-deploy), cost optimization (use azure-cost), or non-Azure clouds." license: MIT metadata: author: Microsoft From ded8feca9490de472954a8ff7d6f4abdaf6fc3ff Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:20:01 -0400 Subject: [PATCH 2/3] Update version to 1.0.8 in plugin.json --- .github/plugins/azure-skills/.claude-plugin/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/plugins/azure-skills/.claude-plugin/plugin.json b/.github/plugins/azure-skills/.claude-plugin/plugin.json index 18da3b72..285706fa 100644 --- a/.github/plugins/azure-skills/.claude-plugin/plugin.json +++ b/.github/plugins/azure-skills/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "azure", "description": "Microsoft Azure MCP integration for cloud resource management, deployments, and Azure services. Manage your Azure infrastructure, monitor applications, and deploy resources directly from Claude Code.", - "version": "1.0.7", + "version": "1.0.8", "author": { "name": "Microsoft", "url": "https://www.microsoft.com" From 08a20bb8cb6891b94c9e0a30eb26c87b52182053 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:20:17 -0400 Subject: [PATCH 3/3] Bump version from 1.0.7 to 1.0.8 --- .github/plugins/azure-skills/.plugin/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/plugins/azure-skills/.plugin/plugin.json b/.github/plugins/azure-skills/.plugin/plugin.json index d9f842c4..7322f09f 100644 --- a/.github/plugins/azure-skills/.plugin/plugin.json +++ b/.github/plugins/azure-skills/.plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "azure", "description": "Microsoft Azure MCP integration for cloud resource management, deployments, and Azure services. Manage your Azure infrastructure, monitor applications, and deploy resources directly from your development environment.", - "version": "1.0.7", + "version": "1.0.8", "author": { "name": "Microsoft", "url": "https://www.microsoft.com"