| name | aws-billing |
| description | Use when working with Aws Billing — analyze, break down, and report AWS costs
and bills. Covers cost breakdown by service, account, or usage type;
monthly/daily billing trends; cost anomaly detection; RI/SP utilization; cost
forecasting; credit/discount analysis; and multi-account cost comparison. Uses
anti-hallucination rules, mandatory currency/credit detection workflow, and
reusable Cost Explorer functions.
|
| connection_type | aws |
| preload | false |
AWS Billing Skill
Analyze AWS billing data with anti-hallucination guardrails and reusable Cost Explorer functions.
Relationship to other AWS skills:
aws-billing/ → "What to analyze" (anti-hallucination rules, reusable billing functions)
aws/ → "How to execute" (parallel patterns, throttling, output format)
aws-pricing/ → "How much things cost" (on-demand pricing via get_aws_cost)
CRITICAL: Billing Data Interpretation Rules (Anti-Hallucination)
These rules are MANDATORY when analyzing AWS billing data. Violating them produces wildly incorrect cost reports.
Rule 1: NET COST Requires the Correct Metric
AWS Cost Explorer exposes 5 cost metrics. The correct default is NetUnblendedCost, which reflects actual charges after all discounts and credits. Never report UnblendedCost alone — it shows charges before negotiated discounts, credits, and RI/SP adjustments.
WRONG: --metrics "UnblendedCost" → Overstates costs
WRONG: --metrics "BlendedCost" → Only meaningful for consolidated billing families
CORRECT: --metrics "NetUnblendedCost" → Actual charges after discounts/credits
The aws_billing_* functions enforce NetUnblendedCost by default via the _AWS_DEFAULT_METRIC constant.
Rule 2: Understand the 5 AWS Cost Metrics
| Metric | What It Represents | When To Use |
|---|
| BlendedCost | Weighted average rate across a consolidated billing family | Multi-account cost allocation reports (rarely useful for analysis) |
| UnblendedCost | On-demand equivalent cost before any discounts | Comparing against on-demand pricing; calculating "what would this cost without discounts" |
| AmortizedCost | Cost with RI/SP upfront fees spread over the term | Budgeting and forecasting; shows true economic cost of RI/SP commitments |
| NetAmortizedCost | AmortizedCost minus credits and negotiated discounts | Most accurate for financial planning with RI/SP commitments |
| NetUnblendedCost | UnblendedCost minus credits and negotiated discounts | DEFAULT for analysis. Shows actual charges on the bill |
Key relationships:
UnblendedCost - Discounts/Credits = NetUnblendedCost
AmortizedCost - Discounts/Credits = NetAmortizedCost
AmortizedCost = UnblendedCost + RI/SP upfront amortization adjustment
Rule 3: Always Detect Account Context First
AWS caller identity determines what billing data you can see:
- Management (payer) account: Sees all linked accounts in the organization
- Member (linked) account: Sees only its own costs (unless delegated access is enabled)
MANDATORY first query: Run aws_billing_account to detect:
- Which account you're operating as (payer vs. member)
- The billing currency
- Which linked accounts are visible
Without this context, you may unknowingly aggregate costs across unrelated accounts or miss accounts entirely.
Rule 4: Sanity-Check Costs Against Known Pricing
Before reporting any cost figure, verify it's physically possible.
Step 1: Check the currency. AWS billing is typically USD, but organizations may have custom billing currencies. The Unit field in CE responses reveals the currency.
Step 2: Verify magnitude against known pricing:
| Resource | Region | Approximate Monthly On-Demand (USD) |
|---|
| t3.micro | us-east-1 | ~$8/mo |
| t3.small | us-east-1 | ~$15/mo |
| t3.medium | us-east-1 | ~$30/mo |
| m5.large | us-east-1 | ~$70/mo |
| m5.xlarge | us-east-1 | ~$140/mo |
| r5.large | us-east-1 | ~$91/mo |
| db.t3.micro (RDS) | us-east-1 | ~$15/mo |
| db.r5.large (RDS) | us-east-1 | ~$175/mo |
| NAT Gateway | us-east-1 | ~$32/mo + $0.045/GB |
| ALB | us-east-1 | ~$16/mo + LCU charges |
Red flags that indicate a query error or currency mismatch:
- A single EC2 instance shows >$1,000/week (t3.medium costs ~$30/month)
- Total account cost exceeds $500,000/week for a standard workload
- Numbers are 1000x higher than expected (possible currency mismatch)
If numbers seem unreasonably high, STOP and verify the currency before reporting.
Rule 5: Detect Credits/Discounts Before Alerting
If NetUnblendedCost is much lower than UnblendedCost, the account has active discounts (RI, SP, EDP, credits). This is normal — not an anomaly.
MANDATORY second query: Run aws_billing_credits FIRST to detect:
discount_ratio: (Unblended - NetUnblended) / Unblended
ri_sp_amortization_effect: Amortized - Unblended
Interpretation:
| discount_ratio | Meaning |
|---|
| > 0.5 | Heavy credit/discount coverage (RI/SP/EDP/credits) — costs are heavily discounted |
| 0.1 – 0.5 | Moderate discounts (typical for RI/SP usage) |
| < 0.1 | Minimal discounts — mostly on-demand pricing |
If discount_ratio > 0.5: Report that the account has significant discounts. Do NOT generate alarmist alerts about cost levels unless the user specifically asks for on-demand equivalent analysis.
Rule 6: Anomaly Detection on Net Cost Only
UnblendedCost fluctuates when RI/SP coverage changes (e.g., an RI expires, a new SP starts). These fluctuations are not real spending anomalies — they're accounting changes.
WRONG: Detect anomalies on UnblendedCost → false positives from RI/SP changes
CORRECT: Detect anomalies on NetUnblendedCost → real spending changes
The aws_billing_anomalies function enforces this by using _AWS_DEFAULT_METRIC (NetUnblendedCost).
Rule 7: Use MONTHLY Granularity by Default
DAILY granularity with SERVICE grouping produces N_days × N_services rows — easily 1,000+ lines for 30 days. This wastes tokens and provides no additional insight for most analyses.
WRONG: --granularity DAILY for 30 days with GROUP_BY SERVICE → token explosion
CORRECT: --granularity MONTHLY + awk aggregation + sort -rn | head -15
Only use DAILY granularity when:
- The user specifically requests daily breakdown
- You're doing anomaly detection (requires daily data points)
- The date range is ≤ 7 days
HOURLY granularity exists but is limited to the past 14 days and only available for EC2 instance-level data. Avoid HOURLY unless the user specifically needs sub-day resolution for a narrow window.
The aws_billing_trend function defaults to MONTHLY and only switches to DAILY with --daily flag.
Rule 8: Cost Explorer Has Limited Lookback
AWS Cost Explorer API retains 13 months + current month of historical data by default. With multi-year data enabled (in Cost Management preferences), up to 38 months at monthly granularity is available. Resource-level data (get-cost-and-usage-with-resources) is limited to 14 days.
Queries beyond the available range return a validation error (not empty results).
WRONG: --days 500 → validation error: start date too old
CORRECT: --days 420 (safe default limit) or --days 365 (safe 12-month lookback)
The _aws_parse_days helper enforces the 420-day limit and returns an error if exceeded. If the organization has multi-year data enabled, you can modify the _AWS_MAX_LOOKBACK_DAYS constant.
Rule 9: CE Date Ranges — Start Inclusive, End Exclusive
Cost Explorer uses half-open intervals: [Start, End).
"Last 30 days" = Start: 30 days ago, End: today
- Start: The first day INCLUDED in the results
- End: The first day EXCLUDED from the results
If you set End = today, today's partial data IS excluded (which is correct — see Rule 10).
The _aws_date_start and _aws_date_end helpers handle this correctly.
Rule 10: Data Has Up To 24 Hour Delay
AWS billing data updates up to 3 times daily but can take up to 24 hours to fully propagate. Today's costs are always incomplete.
Do NOT:
- Alert on "cost drops" for the current day
- Compare today's partial data to yesterday's full data
- Report today's costs as final
The billing functions set End = today (which excludes today due to Rule 9), ensuring only complete data is analyzed.
Rule 11: RI/SP API Dimension Restrictions
Cost Explorer's reservation and savings plan APIs have strict dimension restrictions:
| API | Valid groupBy Dimensions |
|---|
get-reservation-utilization | SUBSCRIPTION_ID only |
get-reservation-coverage | AZ, CACHE_ENGINE, DATABASE_ENGINE, DEPLOYMENT_OPTION, INSTANCE_TYPE, INVOICING_ENTITY, LINKED_ACCOUNT, OPERATING_SYSTEM, PLATFORM, REGION, TENANCY |
get-savings-plans-utilization | No groupBy support |
get-savings-plans-coverage | INSTANCE_FAMILY, REGION, SERVICE |
Using unsupported dimensions causes API errors. For SERVICE-level analysis, use get-cost-and-usage instead.
Additional restrictions:
get-reservation-utilization defaults to EC2 if no SERVICE filter is specified. To check RDS/ElastiCache/Redshift RI utilization, you must explicitly pass a SERVICE filter.
get-reservation-utilization/get-reservation-coverage filters only support AND between dimensions (no OR/NOT combinations), nesting max 1 level deep.
get-savings-plans-utilization/get-savings-plans-coverage filters only support AND between dimensions.
Rule 12: Tax Appears as a Separate Service
AWS reports taxes (VAT, consumption tax, sales tax) as a separate service line called "Tax" in Cost Explorer. This is not a cloud service — it's a billing artifact.
NetUnblendedCost does NOT exclude taxes (it only excludes credits/discounts). Taxes are real charges.
- "Tax" may appear in
aws_billing_summary as a top service. Do not confuse it with compute/storage spend.
- When comparing AWS costs to GCP, note that GCP's
cost_type separates tax from regular charges, while AWS mixes them into the same metric.
- For tax-specific analysis, filter by
SERVICE = "Tax" to isolate tax charges.
Mandatory Pre-Analysis Checklist
Before writing ANY billing analysis, verify ALL of the following:
Billing Script (get_billing_aws.sh)
DO NOT read or modify the script file. Only source and call the functions.
SETUP (at the start of your script):
source ./_skills/connections/aws/aws-billing/scripts/get_billing_aws.sh
All functions enforce anti-hallucination rules: NetUnblendedCost default metric, account filter when specified, 420-day lookback limit, TOON output format.
FUNCTION REFERENCE:
| Function | Purpose | Signature |
|---|
aws_billing_account | Detect caller, currency, linked accounts (MANDATORY first) | [--days N] |
aws_billing_credits | Credit/discount program detection (MANDATORY second) | [--account ID] [--days N] |
aws_billing_summary | Top 15 services by net cost | [--account ID] [--days N] |
aws_billing_trend | Cost trend (monthly/daily) | [--account ID] [--days N] [--daily] |
aws_billing_anomalies | Z-score anomaly on net cost (45-day lookback) | [--account ID] |
aws_billing_by_usage_type | Top 20 usage types by net cost | [--account ID] [--days N] |
aws_billing_forecast | CE cost forecast with prediction intervals | [--account ID] [--days N] |
aws_billing_ri_sp | RI and Savings Plan utilization | [--account ID] [--days N] |
aws_billing_compare | Multi-account comparison (no account filter) | [--days N] |
MANDATORY WORKFLOW (every billing analysis):
- Always run
aws_billing_account first to detect caller identity, currency, and linked accounts (see Rule 3)
- Always run
aws_billing_credits second to detect discount programs (see Rule 5)
- Interpret
discount_ratio: > 0.5 = heavily discounted (do NOT alarm); 0.1–0.5 = moderate discounts; < 0.1 = minimal discounts
- Then run
aws_billing_summary or other functions as needed
- Sanity-check costs against known AWS pricing (see Rule 4)
Examples:
source ./_skills/connections/aws/aws-billing/scripts/get_billing_aws.sh
aws_billing_account
aws_billing_credits
aws_billing_summary --days 30
aws_billing_summary --account 123456789012 --days 30
aws_billing_trend --days 90
aws_billing_trend --days 14 --daily
aws_billing_anomalies
aws_billing_by_usage_type --days 30
aws_billing_forecast --days 30
aws_billing_ri_sp --days 90
aws_billing_compare --days 30
CLI Execution Patterns
The aws_billing_* functions handle CE queries correctly by default. For custom aws ce commands, read docs/ce-cli-patterns.md in this skill directory for:
- Cost Explorer output aggregation and token efficiency rules
- Heredoc syntax pitfalls (options MUST come before
<<EOF)
- CloudTrail event parsing with
jq fromjson
Cost Metrics Deep Reference
UnblendedCost
The cost of usage at the rate the account is charged — i.e., the on-demand rate minus any RI/SP hourly discount, but before credits and negotiated discounts (EDP). For accounts with RI/SP, the RI/SP-covered hours show the discounted RI/SP rate, while uncovered hours show on-demand rate.
When to use: Comparing against on-demand pricing; understanding what each resource "costs" at its charged rate.
NetUnblendedCost
UnblendedCost minus credits, EDP discounts, and other negotiated adjustments. This is what actually appears on your bill.
When to use: Default for all analysis. Shows actual charges.
BlendedCost
The weighted average rate across all accounts in a consolidated billing family. AWS computes this by pooling usage across accounts to maximize tiered pricing benefits, then redistributing the blended rate.
When to use: Rarely. Only useful for cross-account cost allocation in organizations that want to distribute volume discounts evenly.
AmortizedCost
Spreads RI/SP upfront payments evenly across the term. For example, a $10,000 1-year All Upfront RI shows ~$27.40/day instead of $10,000 on day 1 and $0 thereafter.
When to use: Budgeting and forecasting; understanding the true daily economic cost of RI/SP commitments.
NetAmortizedCost
AmortizedCost minus credits and negotiated discounts. The most accurate metric for financial planning when you have RI/SP commitments.
When to use: Long-term financial planning with RI/SP commitments; board-level cost reporting.
AWS Pricing Sanity-Check Table
Approximate monthly on-demand costs in USD (us-east-1). Use to validate query results.
| Resource | Type | ~Monthly USD |
|---|
| EC2 | t3.micro | $8 |
| EC2 | t3.small | $15 |
| EC2 | t3.medium | $30 |
| EC2 | m5.large | $70 |
| EC2 | m5.xlarge | $140 |
| EC2 | c5.xlarge | $124 |
| EC2 | r5.large | $91 |
| RDS | db.t3.micro | $15 |
| RDS | db.t3.small | $29 |
| RDS | db.r5.large | $175 |
| ElastiCache | cache.t3.micro | $13 |
| NAT Gateway | per gateway | $32 + data |
| ALB | per ALB | $16 + LCU |
| S3 | Standard/GB | $0.023 |
| EBS | gp3/GB | $0.08 |
| Lambda | 1M req, 1s, 128MB | $2.10 |
If your query shows a single t3.medium costing $3,000/month, something is wrong.
Discount and Credit Types
| Type | Source | Typical Savings | How It Affects Metrics |
|---|
| Reserved Instances (RI) | Upfront commitment (1yr or 3yr) | 30–72% vs on-demand | Lowers UnblendedCost for covered hours; AmortizedCost spreads upfront fee |
| Savings Plans (SP) | Hourly commitment (1yr or 3yr) | 20–72% vs on-demand | Same effect as RI but more flexible (Compute SP covers EC2+Fargate+Lambda) |
| Enterprise Discount Program (EDP) | Spend commitment with AWS | 5–20% off entire bill | Difference between Unblended and NetUnblended |
| Free Tier | Automatic for new accounts (12 months) | 100% for eligible usage | Shows as $0 NetUnblendedCost for covered usage |
| Promotional Credits | AWS programs (startups, migrations) | Up to 100% | Shows as gap between Unblended and NetUnblended |
Key insight: discount_ratio from aws_billing_credits captures the combined effect of ALL discount types. If > 0.5, the account is heavily discounted.
CE API Quick Reference
Filter Syntax
--filter '{"Dimensions":{"Key":"LINKED_ACCOUNT","Values":["123456789012"]}}'
--filter '{"Dimensions":{"Key":"SERVICE","Values":["Amazon Elastic Compute Cloud - Compute"]}}'
--filter '{"And":[{"Dimensions":{"Key":"LINKED_ACCOUNT","Values":["123456789012"]}},{"Dimensions":{"Key":"SERVICE","Values":["Amazon Elastic Compute Cloud - Compute"]}}]}'
--filter '{"Not":{"Dimensions":{"Key":"SERVICE","Values":["AWS Support (Business)"]}}}'
--filter '{"Tags":{"Key":"Environment","Values":["production"]}}'
GroupBy Syntax
--group-by Type=DIMENSION,Key=SERVICE
--group-by Type=DIMENSION,Key=LINKED_ACCOUNT
--group-by Type=DIMENSION,Key=USAGE_TYPE
--group-by Type=TAG,Key=Environment
--group-by Type=DIMENSION,Key=SERVICE Type=DIMENSION,Key=LINKED_ACCOUNT
Heredoc & Output Rules
For raw CE query patterns (heredoc ordering, awk aggregation, output size limits), see docs/ce-cli-patterns.md in this skill directory.
get-cost-forecast Notes
- Uses
--metric (singular), not --metrics (plural)
- Metric names use SCREAMING_SNAKE_CASE (e.g.,
NET_UNBLENDED_COST), NOT CamelCase like get-cost-and-usage
- Valid metric values:
AMORTIZED_COST, BLENDED_COST, NET_AMORTIZED_COST, NET_UNBLENDED_COST, UNBLENDED_COST
- Start date must be tomorrow or later (cannot forecast the past)
- Maximum forecast period: 3 months at DAILY granularity, 18 months at MONTHLY granularity
- Prediction intervals are only available for certain granularities
CE API Pricing
Cost Explorer API charges per paginated request ($0.01/request). Cache results when possible and avoid redundant queries. Use filtering and appropriate granularity to minimize pagination.
Common Errors
| Error | Cause | Solution |
|---|
Start date must be after today | get-cost-forecast start date is today or earlier | Use _aws_date_end_future 1 for start |
The requested time range is not available | Date range exceeds ~14 months | Limit to 420 days max |
ValidationException: groupBy Dimensions | Invalid dimension for RI/SP APIs | See Rule 11 for valid dimensions |
Metrics must contain only one element | get-cost-forecast called with multiple metrics | Use --metric (singular) with one metric |
Value 'NetUnblendedCost' for metric is invalid | Wrong casing for get-cost-forecast metric | Use SCREAMING_SNAKE_CASE: NET_UNBLENDED_COST |
OptIn is not supported for the following metric | NetUnblendedCost not enabled | Enable in Cost Explorer settings (24h activation) |
Access Denied for CE APIs | Member account without delegated access | Check organization delegated administrator settings |
DataUnavailable | Querying dates before CE was activated | CE data starts from account activation date |
BillingPeriodRange / start date too old | Date range beyond available lookback | Reduce to 420 days (or 38mo if multi-year enabled) |
UnrecognizedClientException | STS session expired or invalid credentials | Re-authenticate or re-assume role |
| Costs seem 0 but account is active | Account on Free Tier or full credit coverage | Run aws_billing_credits to check discount_ratio |
| Forecast returns error for long period | Exceeds forecast limit (3mo daily, 18mo monthly) | Reduce forecast period or switch to MONTHLY granularity |
| RI utilization shows only EC2 | get-reservation-utilization defaults to EC2 | Pass --filter with SERVICE dimension for RDS/ElastiCache/Redshift |
Output Format
Present results as a structured report:
Aws Billing Report
══════════════════
Resources discovered: [count]
Resource Status Key Metric Issues
──────────────────────────────────────────────
[name] [ok/warn] [value] [findings]
Summary: [total] resources | [ok] healthy | [warn] warnings | [crit] critical
Action Items: [list of prioritized findings]
Target ≤50 lines of output. Use tables for multi-resource comparisons.
Counter-Rationalizations
| Shortcut | Counter | Why |
|---|
| "I'll skip discovery and check known resources" | Always run Phase 1 discovery first | Resource names change, new resources appear — assumed names cause errors |
| "The user only asked for a quick check" | Follow the full discovery → analysis flow | Quick checks miss critical issues; structured analysis catches silent failures |
| "Default configuration is probably fine" | Audit configuration explicitly | Defaults often leave logging, security, and optimization features disabled |
| "Metrics aren't needed for this" | Always check relevant metrics when available | API/CLI responses show current state; metrics reveal trends and intermittent issues |
| "I don't have access to that" | Try the command and report the actual error | Assumed permission failures prevent useful investigation; actual errors are informative |