Skip to content

Commit a09a7dc

Browse files
committed
feat(azure-cost): add App Service optimization guide
- Add services/appservice/azure-app-service.md with plan rightsizing, idle slot detection, dev/test pricing, and Resource Graph queries - Consolidate service routing in workflow.md into compact table format - Add 2 vally eval stimuli for App Service cost routing - Trim storage intro and SKILL.md to stay within token budgets Partial progress on #2524
1 parent 54e0f11 commit a09a7dc

5 files changed

Lines changed: 140 additions & 17 deletions

File tree

evals/azure-cost/eval.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,48 @@ stimuli:
371371
config:
372372
pattern: "(?i)fatal error|unhandled exception|stack trace"
373373

374+
# ═══════════════════════════════════════════
375+
# App Service Cost Optimization routing prompts
376+
# ═══════════════════════════════════════════
377+
378+
# ── appservice-idle-slots-prompt ──
379+
# Assertions: softCheckSkill + isSkillInvoked (invocation rate ≥ 80%)
380+
- name: "App Service idle deployment slots cost"
381+
prompt: "I have deployment slots on my App Service that aren't being used, how much are they costing me?"
382+
tags:
383+
type: integration
384+
tier: full
385+
cost: llm
386+
area: routing
387+
earlyTerminate: '[{"type":"skill-call","skill":"azure-cost"},{"type":"tool-call-count","count":3}]'
388+
graders:
389+
- type: skill-invocation
390+
config:
391+
required:
392+
- azure-cost
393+
- type: output-not-matches
394+
config:
395+
pattern: "(?i)fatal error|unhandled exception|stack trace"
396+
397+
# ── appservice-plan-downgrade-prompt ──
398+
# Assertions: softCheckSkill + isSkillInvoked (invocation rate ≥ 80%)
399+
- name: "App Service plan downgrade savings"
400+
prompt: "Can I save money by downgrading my Premium App Service plans in dev/test to a cheaper tier?"
401+
tags:
402+
type: integration
403+
tier: full
404+
cost: llm
405+
area: routing
406+
earlyTerminate: '[{"type":"skill-call","skill":"azure-cost"},{"type":"tool-call-count","count":3}]'
407+
graders:
408+
- type: skill-invocation
409+
config:
410+
required:
411+
- azure-cost
412+
- type: output-not-matches
413+
config:
414+
pattern: "(?i)fatal error|unhandled exception|stack trace"
415+
374416
# ═══════════════════════════════════════════
375417
# Response quality tests
376418
# ═══════════════════════════════════════════

plugin/skills/azure-cost/SKILL.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,11 @@ Query historical costs, forecast future spending, optimize to reduce waste.
3232
- Subscription: `/subscriptions/<id>`
3333
- Resource Group: `/subscriptions/<id>/resourceGroups/<name>`
3434
- Management Group: `/providers/Microsoft.Management/managementGroups/<id>`
35-
- Billing Account: `/providers/Microsoft.Billing/billingAccounts/<id>`
3635

37-
## Service-Specific Optimization
36+
## Service Optimization Guides
3837

39-
- [Redis](cost-optimization/services/redis/azure-cache-for-redis.md)
40-
- [Storage](cost-optimization/services/storage/azure-storage.md)
38+
[Redis](cost-optimization/services/redis/azure-cache-for-redis.md) | [Storage](cost-optimization/services/storage/azure-storage.md) | [App Service](cost-optimization/services/appservice/azure-app-service.md)
4139

4240
## References
4341

4442
- [MCP Tools, Best Practices, Safety](references/tools-and-best-practices.md)
45-
- [SDK: Redis .NET](cost-optimization/sdk/azure-resource-manager-redis-dotnet.md)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
## Azure App Service Cost Optimization
2+
3+
Reference guide for reducing App Service costs through plan rightsizing, idle slot cleanup, and dev/test pricing.
4+
5+
## Cost Optimization Rules
6+
7+
| Priority | Rule | Detection Logic | Recommendation | Avg Savings |
8+
|----------|------|----------------|----------------|-------------|
9+
| 🔴 Critical | Stopped App on Paid Plan | `state == 'Stopped'` AND `sku.tier != 'Free/Shared'` | Delete or move to Free tier (stopped apps still incur plan cost) | $50-500/mo |
10+
| 🔴 Critical | Empty App Service Plan | Plan has zero apps deployed | Delete the plan | $50-400/mo |
11+
| 🟠 High | Premium in Non-Production | `sku.tier in ['PremiumV2','PremiumV3']` AND `tags.environment in ['dev','test','staging']` | Downgrade to Basic or Standard | $100-600/mo |
12+
| 🟠 High | Idle Deployment Slots | Non-production slots with zero traffic for 14+ days | Delete unused slots (each slot = separate app instance cost) | $50-300/mo |
13+
| 🟠 High | Over-Provisioned Plan | CPU avg <20% AND memory avg <30% over 14 days | Scale down SKU or reduce instance count | $50-400/mo |
14+
| 🟡 Medium | No Auto-Scale Rules | Production plan with fixed instance count >2 | Add auto-scale rules to scale in during low traffic | $30-200/mo |
15+
| 🟡 Medium | Missing Dev/Test Pricing | Dev/test workloads on regular pricing | Enable Dev/Test pricing via subscription offer | 30-55% savings |
16+
| 🟡 Medium | Always On for Non-Production | `alwaysOn == true` on dev/test apps | Disable Always On (apps cold-start on first request) | Reduced idle cost |
17+
| 🟢 Low | Untagged App Service | Missing `environment`, `owner`, or `costCenter` tags | Apply tags for cost allocation | N/A |
18+
| 🟢 Low | Old Deployment Slots | Slots older than 90 days not used for blue-green | Review if still needed | Variable |
19+
20+
## Plan Tier Decision Matrix
21+
22+
| Workload | Recommended Tier | Key Features |
23+
|----------|-----------------|--------------|
24+
| Dev/test, prototypes | Free / Basic B1 | No SLA, limited scale |
25+
| Low-traffic production | Standard S1 | Auto-scale, slots, backups |
26+
| High-traffic production | Premium P1v3 | Better perf, more slots, VNET |
27+
| Isolated compliance | Isolated I1v2 | Private environment, max scale |
28+
29+
## Resource Graph Queries
30+
31+
**Find stopped apps on paid plans:**
32+
33+
```kql
34+
Resources
35+
| where type =~ 'microsoft.web/sites'
36+
| where properties.state =~ 'Stopped'
37+
| where isnotempty(properties.serverFarmId)
38+
| project name, resourceGroup, kind, state=properties.state
39+
```
40+
41+
**Find empty App Service Plans:**
42+
43+
```kql
44+
Resources
45+
| where type =~ 'microsoft.web/serverfarms'
46+
| where properties.numberOfSites == 0
47+
| project name, resourceGroup, sku=sku.name, location
48+
```
49+
50+
**Find Premium plans in non-production:**
51+
52+
```kql
53+
Resources
54+
| where type =~ 'microsoft.web/serverfarms'
55+
| where sku.tier in~ ('PremiumV2', 'PremiumV3', 'Premium')
56+
| where tags.environment in~ ('dev', 'test', 'staging', 'sandbox')
57+
| project name, resourceGroup, sku=sku.name, tier=sku.tier, tags
58+
```
59+
60+
## Tools & Commands
61+
62+
**MCP Tool:** `azure__appservice` for listing and managing App Service resources
63+
64+
**Azure CLI:**
65+
- `az appservice plan list --resource-group <rg>` - List plans
66+
- `az appservice plan show --name <plan> --resource-group <rg>` - Plan details
67+
- `az webapp list --resource-group <rg>` - List web apps
68+
- `az webapp show --name <app> --resource-group <rg>` - App details
69+
- `az webapp deployment slot list --name <app> --resource-group <rg>` - List slots
70+
- `az monitor metrics list --resource <plan-id> --metric CpuPercentage --interval PT1H` - CPU utilization
71+
- `az monitor metrics list --resource <plan-id> --metric MemoryPercentage --interval PT1H` - Memory utilization
72+
73+
## Pricing Quick Reference
74+
75+
Approximate monthly costs (Linux, East US):
76+
- **Free F1**: $0 (60 min CPU/day, 1 GB RAM)
77+
- **Basic B1**: ~$13/mo (1 core, 1.75 GB)
78+
- **Standard S1**: ~$69/mo (1 core, 1.75 GB, auto-scale, slots)
79+
- **Premium P1v3**: ~$138/mo (2 cores, 8 GB, better perf)
80+
81+
Each deployment slot runs as a separate instance at full plan cost. Windows plans cost ~30% more than Linux.
82+
83+
Always validate from [official pricing](https://azure.microsoft.com/pricing/details/app-service/).

plugin/skills/azure-cost/cost-optimization/services/storage/azure-storage.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Azure Storage Cost Optimization
22

3-
Reference guide for identifying cost savings opportunities in Azure Storage accounts through tier analysis, lifecycle policies, and orphaned resource detection.
3+
Identify cost savings in Azure Storage through tier analysis, lifecycle policies, and orphaned resource detection.
44

55
## Subscription Input Options
66

plugin/skills/azure-cost/cost-optimization/workflow.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,14 @@ azure__get_azure_bestpractices({
5757

5858
Wait for user response before proceeding.
5959

60-
## Step 1.7: Storage-Specific Analysis (Conditional)
60+
## Steps 1.7–1.75: Service-Specific Analysis (Conditional)
6161

62-
**If the user requests Storage cost optimization**, load: [Azure Storage Cost Optimization](./services/storage/azure-storage.md)
62+
For service-focused requests, load the relevant guide and follow it. For general optimization, skip to Step 2.
6363

64-
**Triggers:** "storage account cost", "blob storage savings", "LRS/GRS/ZRS downgrade", "storage lifecycle savings", "reduce storage spending".
65-
66-
For Storage-only requests, follow the Storage reference. For general optimization that includes storage, continue to Step 2.
64+
| Triggers | Reference |
65+
|----------|-----------|
66+
| "storage account cost", "blob savings", "LRS/GRS downgrade", "storage lifecycle savings" | [Storage](./services/storage/azure-storage.md) |
67+
| "app service cost", "plan savings", "web app spending", "idle slots", "overprovisioned plan" | [App Service](./services/appservice/azure-app-service.md) |
6768

6869
## Step 1.8: AKS-Specific Analysis (Conditional)
6970

@@ -75,14 +76,14 @@ For Storage-only requests, follow the Storage reference. For general optimizatio
7576
- User reports a cost spike, unusual cluster utilization, or wants budget alerts
7677

7778
**Tool Selection:**
78-
- **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
79-
- **Fall back to CLI**: Use `az aks` and `kubectl` only when the specific operation cannot be performed via the MCP surface
79+
- **Prefer MCP first**: Use `azure__aks` for AKS operations (list clusters, get node pools, inspect configuration)
80+
- **Fall back to CLI**: Use `az aks` and `kubectl` only when MCP doesn't cover the operation
8081

81-
**Reference files (load only what is needed for the request):**
82-
- [Cost Analysis Add-on](./azure-aks-cost-addon.md) — enable namespace-level cost visibility
83-
- [Anomaly Investigation](./azure-aks-anomalies.md) — cost spikes, scaling events, budget alerts
82+
**Reference files:**
83+
- [Cost Analysis Add-on](./azure-aks-cost-addon.md)
84+
- [Anomaly Investigation](./azure-aks-anomalies.md)
8485

85-
> **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.
86+
For general optimization (including AKS resource groups), continue to Step 2.
8687

8788
## Step 1.9: Choose Analysis Scope (for AKS-specific analysis)
8889

0 commit comments

Comments
 (0)