Skip to content
Learni
View all tutorials
Cloud & DevOps

Comment implémenter l'allocation des coûts cloud AWS en 2026

Introduction

L'allocation des coûts cloud est cruciale en 2026 pour les entreprises gérant des environnements AWS multi-équipes : elle permet d'attribuer précisément les dépenses (EC2, S3, RDS) à des projets, départements ou environnements, favorisant la responsabilité financière (FinOps). Sans cela, les factures AWS gonflent sans visibilité, menant à des surcoûts de 30-50% selon Gartner.

Ce tutoriel expert vous guide pas à pas : configuration de Cost and Usage Reports (CUR) pour données granulaires, politiques de tags obligatoires via Terraform, requêtes boto3 pour extraire les coûts par tag, et analyses SQL Athena pour allocations avancées. Imaginez un tableau de bord où l'équipe DevOps voit ses EC2 à 2 500 €/mois vs. Prod à 15 000 € – c'est ce que nous construisons.

À la fin, vous aurez un pipeline automatisable produisant des CSV alloués mensuels, intégrables à QuickSight ou Excel. Basé sur AWS Cost Explorer API et CUR (données historiques jusqu'à 14 mois), ce setup scale pour des milliards de $ de spend. Prêt à optimiser vos 100k€+ annuels ? (128 mots)

Prérequis

  • Compte AWS avec droits Billing et Organizations (admin).
  • AWS CLI v2 installé et configuré (aws configure).
  • Python 3.12+ avec pip install boto3 pandas openpyxl.
  • Terraform 1.9+ pour les policies.
  • Accès S3/Athena pour CUR (activez dans console Billing > Cost Reports).

Installer et configurer AWS CLI + boto3

setup.sh
#!/bin/bash

# Installer AWS CLI v2 (macOS/Linux)
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Configurer avec MFA si besoin
aws configure

# Installer Python deps
pip install boto3 pandas openpyxl 'boto3-stubs[essential]'

# Vérifier
aws sts get-caller-identity
python -c "import boto3; print('boto3 OK')"

Ce script bash complet initialise AWS CLI et boto3 en une exécution. Il gère l'install sans sudo excessif, configure les creds (Access Key + Secret), et teste l'accès. Piège : oubliez MFA pour prod – utilisez IAM roles EC2/Lambda à la place.

Configurer Cost and Usage Report (CUR)

Activez CUR dans la console AWS Billing > Cost & Usage Reports : choisissez S3 bucket dédié, format Parquet/ORC, incluez tags (jusqu'à 50). Délai : 24h pour premiers data. Cela génère des tables Athena comme cur_table avec colonnes line_item_usage_account_id, line_item_product_code, resource_tags_user_key.

Analogie : CUR est votre 'grand livre comptable' AWS – sans tags, c'est du bruit ; avec, c'est de l'or pour allouer 95% des coûts.

Terraform : Politique de tags obligatoires

tags_policy.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "eu-west-1"
}

resource "aws_organizations_policy" "tag_policy" {
  name        = "RequireCostTags"
  description = "Oblige tags: Environment, Team, Project"
  type        = "TAG_POLICY"

  document = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "DenyUntaggedResources"
        Effect    = "Deny"
        Resources = ["*"]
        Actions   = [
          "ec2:RunInstances",
          "rds:CreateDBInstance",
          "s3:CreateBucket"
        ]
        Condition = {
          Null = {
            "aws:RequestTag/Environment" = "true"
            "aws:RequestTag/Team"        = "true"
            "aws:RequestTag/Project"     = "true"
          }
          ForAnyValue:StringEquals = {
            "aws:TagKeys" = ["Environment", "Team", "Project"]
          }
        }
      }
    ]
  })
}

# Appliquer à OU
resource "aws_organizations_policy_attachment" "attach" {
  policy_id = aws_organizations_policy.tag_policy.id
  target_id = data.aws_organizations_organization.this.id
}

data "aws_organizations_organization" "this" {}

Ce module Terraform déploie une Tag Policy AWS Organizations bloquant les créations sans tags Environment, Team, Project. Exécutez terraform init/apply sur compte management. Piège : tags propagés en 1-24h ; testez avec EC2 deny. Couvre 80% des services AWS.

Terraform : Ressource EC2 taggée exemple

ec2_tagged.tf
resource "aws_instance" "example" {
  ami           = "ami-0c02fb55956c7d316" # Amazon Linux 2023 eu-west-1
  instance_type = "t3.micro"

  tags = {
    Name        = "cost-allocation-demo"
    Environment = "dev"
    Team        = "devops"
    Project     = "finops-tutorial"
  }

  # Security group minimal
  vpc_security_group_ids = [aws_security_group.ssh.id]
}

resource "aws_security_group" "ssh" {
  name_prefix = "ssh-demo"
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

output "instance_id" {
  value = aws_instance.example.id
}

Déployez cette EC2 taggée via terraform apply : génère des coûts réels allouables. Tags mappent à CUR ; security group basique pour SSH. Piège : changez AMI par région ; supprimez après test (terraform destroy) pour éviter frais.

Extraire les coûts par tag avec boto3

Utilisez Cost Explorer API : filtre par TAG_KEY (ex: 'Team'), groupBy pour granularité. Limite : 500 résultats ; paginez avec NextPageToken. Pour CUR full, query Athena (suivant).

Script Python : Coûts par tag (Cost Explorer)

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

ce = boto3.client('ce')

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

# Tags à analyser
TAGS = ['Environment', 'Team', 'Project']

results = []
for tag in TAGS:
    resp = ce.get_cost_and_usage(
        TimePeriod={'Start': start.strftime('%Y-%m-%d'), 'End': end.strftime('%Y-%m-%d')},
        Granularity='MONTHLY',
        Metrics=['UnblendedCost'],
        GroupBy=[{'Type': 'DIMENSION', 'Key': 'SERVICE'}, {'Type': 'TAG', 'Key': tag}],
        Filter={'Tags': {'Key': tag, 'Values': ['*']}}
    )
    for group in resp['ResultsByTime'][0]['Groups']:
        results.append({
            'Tag': tag,
            'Service': group['Keys'][1] if len(group['Keys']) > 1 else 'N/A',
            'Value': group['Keys'][0],
            'Cost': float(group['Metrics']['UnblendedCost']['Amount'])
        })

df = pd.DataFrame(results)
df.to_excel('costs_by_tag.xlsx', index=False)
print(df.groupby('Tag').sum())

Ce script fetch les coûts mensuels par tag/service via CostExplorer, exporte Excel. Filtre dynamique sur tags ; gère multi-groupBy. Piège : activez CostExplorer (console) ; coûts >1$/mois pour visibilité ; rate limit 10 req/s.

Script Python : Allocation avancée par équipe

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

ce = boto3.client('ce')
athena = boto3.client('athena')
s3 = boto3.client('s3')

# Config Athena (adaptez)
DB = 'cur_db'
TABLE = 'cur_table'
OUTPUT_S3 = 's3://your-cur-bucket/query_results/'

# Query SQL pour allocation Team
query = f"""
SELECT 
  line_item_usage_account_id,
  resource_tags_user_Environment,
  resource_tags_user_Team,
  SUM(line_item_unblended_cost) as total_cost,
  line_item_product_code
FROM {TABLE}
WHERE line_item_usage_start_date >= date('{ (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')}')
  AND line_item_usage_start_date < date('{datetime.now().strftime('%Y-%m-%d')}')
  AND resource_tags_user_Team IS NOT NULL
GROUP BY 1,2,3,4
ORDER BY total_cost DESC
"""

# Exécuter query
resp = athena.start_query_execution(
    QueryString=query,
    QueryExecutionContext={'Database': DB},
    ResultConfiguration={'OutputLocation': OUTPUT_S3}
)
query_id = resp['QueryExecutionId']

# Poll status et fetch (simplifié)
import time
while True:
    status = athena.get_query_execution(QueryExecutionId=query_id)['QueryExecution']['Status']['State']
    if status in ['SUCCEEDED', 'FAILED', 'CANCELLED']:
        break
    time.sleep(5)

if status == 'SUCCEEDED':
    results = athena.get_query_results(QueryExecutionId=query_id)
    df = pd.DataFrame([row['Data'] for row in results['ResultSet']['Rows'][1:]])
    df.to_csv('team_allocation.csv', index=False)
    print('Rapport généré : team_allocation.csv')

# Coût total par team via CE pour validation
resp_ce = ce.get_cost_and_usage(
    TimePeriod={'Start': '2026-01-01', 'End': '2026-01-31'},
    Granularity='MONTHLY',
    Metrics=['UnblendedCost'],
    GroupBy=[{'Type': 'TAG', 'Key': 'Team'}]
)
print('Validation CE:', resp_ce)

Hybride CE + Athena : query CUR pour tags détaillés (non supportés fully par CE), export CSV. Adaptez DB/TABLE post-CUR setup. Piège : Parquet schema évolue ; testez query console Athena d'abord ; coûts query ~0.005$/TB scanné.

Query SQL Athena pour CUR allocation

cur_allocation.sql
-- Exécutable directement en console Athena
SELECT
  'Team' AS allocation_key,
  resource_tags_user_Team AS key_value,
  SUM(CASE WHEN line_item_product_code LIKE '%EC2%' THEN line_item_unblended_cost ELSE 0 END) AS ec2_cost,
  SUM(CASE WHEN line_item_product_code LIKE '%S3%' THEN line_item_unblended_cost ELSE 0 END) AS s3_cost,
  SUM(line_item_unblended_cost) AS total_cost,
  COUNT(*) AS resources_count
FROM cur_table
WHERE
  line_item_usage_start_date >= DATE '2026-01-01'
  AND line_item_usage_start_date < DATE '2026-02-01'
  AND resource_tags_user_Team IS NOT NULL
  AND line_item_line_item_type = 'Usage'
GROUP BY resource_tags_user_Team
HAVING total_cost > 0

UNION ALL

SELECT
  'Environment' AS allocation_key,
  resource_tags_user_Environment AS key_value,
  SUM(line_item_unblended_cost) AS total_cost,
  COUNT(*) AS resources_count
FROM cur_table
WHERE
  line_item_usage_start_date >= DATE '2026-01-01'
  AND line_item_usage_start_date < DATE '2026-02-01'
  AND resource_tags_user_Environment IN ('prod', 'dev')
GROUP BY resource_tags_user_Environment

ORDER BY allocation_key, total_cost DESC;

Query UNION pour multi-tags : segmente EC2/S3 par Team/Env, filtre Usage only. Copiez-collez en Athena post-CUR. Piège : linked_account pour multi-acct ; ajoutez WHERE line_item_usage_type = 'BoxUsage:c6a.4xlarge' pour granularité instance.

Bonnes pratiques

  • Tags std : 3-5 max (Env, Team, Owner, App) ; utilisez AWS Tag Editor pour retro-tag.
  • Automate : Lambda CloudWatch mensuel sur scripts Python + SNS alert >budget.
  • CUR optim : Parquet + Glue partitionné par date ; Athena Workgroups pour coûts query.
  • FinOps : Intégrez QuickSight dataset CUR ; set budgets par tag via Budgets API.
  • Multi-cloud : Répliquez avec GCP Labels API ou Azure Tags.

Erreurs courantes à éviter

  • Tags non propagés : Délai 24h ; ne pas compter sur real-time pour allocation.
  • CUR incomplet : Oublie RI/SP ; activez 'Include RI recommendations'.
  • Over-query Athena : Scan TB inutiles ; partitionnez dt=YYYY-MM-DD et use WHERE dt>='...' .
  • Permissions : IAM policy sans ce:GetCostAndUsage ou athena:StartQueryExecution échoue silently.

Pour aller plus loin