Skip to content
Learni
View all tutorials
Cloud AWS

How to Master AWS Cost Management in 2026

Lire en français

Introduction

AWS cost management is crucial in 2026, as multi-region deployments and AI workloads explode budgets. Without advanced tools, bills can double in a month. This advanced tutorial guides you through automating analysis with the Cost Explorer API, setting up budgets with SNS notifications, enabling cost allocation tags, and deploying Boto3 scripts for custom reports. Imagine an automated dashboard that alerts on EC2 overruns before they hit your P&L. We start with CLI basics and build up to serverless Lambdas. By the end, you'll master workflows that typically save 20-40% on enterprise accounts. Ready to turn costs into a competitive advantage? (128 words)

Prerequisites

  • AWS account with IAM permissions: CostExplorerFullAccess, BudgetsReadOnlyAccess, and budgets:CreateBudget
  • AWS CLI v2 installed and configured (aws configure)
  • Python 3.12+ with pip install boto3
  • Node.js 20+ for Lambda examples (optional)
  • Advanced knowledge of IAM, SNS, and CloudWatch

Configure AWS CLI and Enable Cost Explorer

terminal
aws configure set region us-east-1
aws configure set output json

# Activer Cost Explorer (une fois par compte)
aws ce update-cost-allocation-tags-status \
  --region us-east-1 \
  --tag-keys Key1=Value1,Key2=Value2 \
  --status ACTIVE

# Vérifier les tags activés
aws ce list-tags-for-cost-allocation --region us-east-1

This script configures the CLI for JSON output and activates two essential cost allocation tags ('Key1', 'Key2') for segmenting costs by project/team. Activation takes 24 hours to propagate; avoid wildcards (*) that clutter queries. Always test with list-tags-for-cost-allocation to confirm.

Understanding Cost Allocation Tags

Cost Allocation Tags turn metadata into billable items. Without them, Cost Explorer lumps everything as 'untagged'. Activate them via CLI for instant granularity, then apply to EC2/S3 resources using User-Defined Tags. Think of it like an accountant categorizing receipts by department.

Create a Programmatic Budget with Notifications

create-budget.sh
#!/bin/bash
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

cat > budget.json << EOF
{
  "BudgetName": "Monthly-Prod-Budget-2026",
  "BudgetLimit": {
    "Amount": "5000",
    "Unit": "USD"
  },
  "PlannedBudgetLimits": {
    "BudgetLimit": {
      "Amount": "4500",
      "Unit": "USD"
    }
  },
  "CostFilters": {
    "Service": ["Amazon EC2"]
  },
  "CostTypes": {
    "IncludeTax": true,
    "IncludeSubscription": true,
    "IncludeSupport": false,
    "IncludeUpfront": true,
    "IncludeCommitment": true
  },
  "TimeUnit": "MONTHLY",
  "TimePeriod": {
    "Start": "2026-01-01T00:00Z",
    "End": "2027-12-31T23:59Z"
  },
  "Notification": {
    "ComparisonOperator": "GREATER_THAN",
    "Threshold": 80,
    "NotificationType": "FORECASTED",
    "NotificationState": "OK"
  },
  "Arn": "arn:aws:sns:us-east-1:${ACCOUNT_ID}:CostAlertTopic"
}
EOF

aws budgets create-budget --account-id $ACCOUNT_ID --budget file://budget.json --cli-input-json file://budget.json
aws budgets create-notification --account-id $ACCOUNT_ID --budget-name Monthly-Prod-Budget-2026 --notification file://budget.json

This complete Bash script creates a monthly EC2 budget of $5000 USD with SNS alerts at 80% forecasted spend. It handles cost types (including taxes) and an annual time period. Pitfall: ensure the SNS ARN exists first, or create it to avoid 400 BadRequest errors.

SNS Integration for Alerts

Budget notifications trigger SNS, which can invoke Lambda or Slack. Set up a dedicated SNS topic (aws sns create-topic --name CostAlertTopic) before running. This makes budgets actionable, not just passive.

Python Boto3 Script for Cost Explorer Queries

cost_analyzer.py
import boto3
import pandas as pd
from datetime import datetime, timedelta

ce = boto3.client('ce', region_name='us-east-1')

# Période : dernier mois
end = datetime.now()
start = end - timedelta(days=30)

group_by = [
    {'Type': 'DIMENSION', 'Key': 'SERVICE'},
    {'Type': 'TAG', 'Key': 'Key1'}
]
metrics = [{'Expression': {'Text': 'SUM({{SERVICE}})', 'Label': 'TotalCost'}, 'Period': 'MONTHLY'}]

response = ce.get_cost_and_usage(
    TimePeriod={'Start': start.strftime('%Y-%m-%d'), 'End': end.strftime('%Y-%m-%d')},
    Granularity='MONTHLY',
    Metrics=['BlendedCost'],
    GroupBy=group_by,
    Filter={'Dimensions': {'Key': 'SERVICE', 'Values': ['Amazon EC2']}}
)

# Convertir en DataFrame pour analyse
rows = []
for group in response['ResultsByTime'][0]['Groups']:
    rows.append({
        'Service': next((d['Value'] for d in group['Keys'] if d.startswith('SERVICE')), 'N/A'),
        'TagKey1': next((d['Value'] for d in group['Keys'] if 'Key1' in d), 'N/A'),
        'Cost': float(group['Metrics']['BlendedCost']['Amount'])
    })
df = pd.DataFrame(rows)
print(df.sort_values('Cost', ascending=False))

df.to_csv('monthly_ec2_costs.csv', index=False)
print('Rapport exporté : monthly_ec2_costs.csv')

This Boto3 script queries last month's EC2 costs by service and Key1 tag, exporting to CSV via Pandas. It uses BlendedCost for RI/SP savings. Pitfall: add Filter for scoping; without Pandas, use json.dumps for raw output.

Advanced Cost Analysis

This query segments by tags, uncovering costly silos (e.g., prod vs. dev). Run it via cron for trends. Analogy: like an IR scanner detecting thermal leaks in a data center.

TypeScript Lambda for Cost Alerts via EventBridge

lambda-cost-handler.ts
import { CostExplorerClient, GetCostAndUsageCommand } from '@aws-sdk/client-cost-explorer';
import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';

const ceClient = new CostExplorerClient({ region: 'us-east-1' });
const snsClient = new SNSClient({ region: 'us-east-1' });

export const handler = async (event: any) => {
  const today = new Date();
  const start = new Date(today.getFullYear(), today.getMonth(), 1).toISOString().split('T')[0];
  const end = today.toISOString().split('T')[0];

  const params = {
    TimePeriod: { Start: start, End: end },
    Granularity: 'DAILY',
    Metrics: ['UnblendedCost'],
    GroupBy: [{ Type: 'DIMENSION', Key: 'SERVICE' }],
    Filter: { CostCategories: { Key: 'Key1', Values: ['Prod'] } }
  };

  try {
    const command = new GetCostAndUsageCommand(params);
    const data = await ceClient.send(command);
    const totalCost = data.ResultsByTime?.[0]?.Groups?.reduce((sum, g) => sum + parseFloat(g.Metrics?.UnblendedCost?.Amount || '0'), 0) || 0;

    if (totalCost > 1000) {
      const snsParams = new PublishCommand({
        TopicArn: process.env.SNS_TOPIC_ARN,
        Message: `Alerte coûts Prod: $${totalCost.toFixed(2)} ce mois.`,
        Subject: 'AWS Cost Alert'
      });
      await snsClient.send(snsParams);
    }
    return { statusCode: 200, body: `Coût total: $${totalCost.toFixed(2)}` };
  } catch (error) {
    console.error(error);
    return { statusCode: 500, body: 'Erreur Cost Explorer' };
  }
};

This TypeScript Lambda, triggered by EventBridge (daily cron), queries Prod costs and sends SNS alerts if over $1000. Uses modular SDK v3. Pitfall: set SNS_TOPIC_ARN as env var; test with sam local invoke to avoid costly cold starts.

Serverless Automation

Deploy this Lambda via SAM/CDK for zero-ops monitoring. EventBridge rule: rate(1 day) on budget events. Scales to 100+ accounts via Organizations.

CloudFormation Stack for Multi-Region Budgets

budgets-stack.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Budgets multi-régions avec SNS'

Parameters:
  BudgetAmount:
    Type: Number
    Default: 5000

Resources:
  CostAlertTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: MultiRegionCostAlerts

  Ec2Budget:
    Type: AWS::Budgets::Budget
    Properties:
      BudgetName: !Sub '${AWS::StackName}-EC2'
      BudgetLimit:
        Amount: !Ref BudgetAmount
        Unit: USD
      TimeUnit: MONTHLY
      CostFilters:
        Service: ['Amazon EC2']
      CostTypes:
        IncludeTax: true
      Notification:
        - ComparisonOperator: GREATER_THAN
          Threshold: 85
          NotificationType: ACTUAL
          NotificationState: ALARM
        - ComparisonOperator: GREATER_THAN
          Threshold: 90
          NotificationType: FORECASTED
          NotificationState: ALARM
      Subscribers:
        - SubscriptionType: SNS
          Address: !Ref CostAlertTopic

Outputs:
  SnsTopicArn:
    Value: !Ref CostAlertTopic
    Export:
      Name: !Sub '${AWS::StackName}-SnsArn'

This CFN template deploys EC2 budgets with dual alerts (actual/forecasted) and integrated SNS. Parameterized for scaling. Pitfall: budgets are global; use --capabilities CAPABILITY_NAMED_IAM on deploy for SNS permissions.

Best Practices

  • Limit to 5-10 tags max: too many dilute Cost Explorer queries.
  • Use RI/SP via CLI: aws ec2 describe-reserved-instances-offerings for 40-70% savings.
  • Cron Boto3 scripts: EventBridge > Lambda for zero infra.
  • Organizations + Control Tower: centralize budgets for 1000+ accounts.
  • QuickSight dashboards: ingest Boto3 CSVs for interactive visuals.

Common Errors to Avoid

  • Forget 24h tag propagation: empty queries; wait or use RI tags.
  • Filter without Dimensions: billions of results; always scope by Service/LinkedAccount.
  • Budgets without Subscribers: silent alerts; validate SNS endpoint.
  • Outdated SDK v2: migrate to v3 for auto-pagination in GetCostAndUsage.

Next Steps

Master AWS Savings Plans with this CLI guide. Explore FinOps via AWS Cost Optimization Hub. Join our Learni Dev AWS Advanced training for pro certifications. Integrate CUR (Cost & Usage Reports) with Athena for massive SQL queries.