Skip to main content
โšก Calmops

Cost Allocation: Chargeback, Showback, FinOps

Introduction

Without proper cost allocation, you’re flying blind. The average enterprise wastes 35% of cloud spend due to lack of visibility and accountability. This guide covers building a FinOps practice that maximizes cloud ROI while maintaining operational excellence.

Key Statistics:

  • Average cloud waste: 35% of total spend
  • Organizations with FinOps save 20-40% on cloud costs
  • Cost allocation improves accountability by 60%
  • Tagging compliance typically starts at 50-70%

FinOps Maturity Model

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     FinOps Maturity Model                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                  โ”‚
โ”‚  Level 4: Optimization (AI/ML)                                 โ”‚
โ”‚  โ”œโ”€โ”€ Predictive cost forecasting                               โ”‚
โ”‚  โ”œโ”€โ”€ Automated right-sizing                                    โ”‚
โ”‚  โ””โ”€โ”€ Continuous optimization                                   โ”‚
โ”‚                                                                  โ”‚
โ”‚  Level 3: Accountability                                        โ”‚
โ”‚  โ”œโ”€โ”€ Chargeback/Showback implemented                           โ”‚
โ”‚  โ”œโ”€โ”€ Budget alerts and enforcement                             โ”‚
โ”‚  โ””โ”€โ”€ Cost anomaly detection                                     โ”‚
โ”‚                                                                  โ”‚
โ”‚  Level 2: Visibility                                            โ”‚
โ”‚  โ”œโ”€โ”€ Cost attribution by team/project                          โ”‚
โ”‚  โ”œโ”€โ”€ Detailed tagging strategy                                 โ”‚
โ”‚  โ””โ”€โ”€ Dashboards and reporting                                  โ”‚
โ”‚                                                                  โ”‚
โ”‚  Level 1: Measurement                                           โ”‚
โ”‚  โ”œโ”€โ”€ Cloud expense tracking                                    โ”‚
โ”‚  โ””โ”€โ”€ Basic cost analysis                                       โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Tagging Strategy

AWS Tagging

# Terraform tagging module
variable "environment" {
  description = "Environment (prod, staging, dev)"
  type        = string
}

variable "team" {
  description = "Team responsible"
  type        = string
}

variable "cost_center" {
  description = "Cost center for billing"
  type        = string
}

variable "project" {
  description = "Project name"
  type        = string
}

variable "owner" {
  description = "Resource owner"
  type        = string
}

locals {
  common_tags = {
    Environment = var.environment
    Team        = var.team
    CostCenter = var.cost_center
    Project     = var.project
    Owner       = var.owner
    ManagedBy   = "Terraform"
  }
}

# Apply tags to resources
resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
  
  tags = merge(local.common_tags, {
    Name = "example-instance"
  })
}

# Enforce tags with SCP
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RequireTags",
      "Effect": "Deny",
      "Action": [
        "ec2:RunInstances",
        "s3:CreateBucket",
        "rds:CreateDBInstance"
      ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestTag/Environment": ["prod", "staging", "dev"],
          "aws:RequestTag/Team": ["*"],
          "aws:RequestTag/CostCenter": ["*"]
        }
      }
    }
  ]
}

Azure Tagging

{
  "properties": {
    "policyDefinitions": [
      {
        "name": "Enforce tag and its value",
        "policyType": "BuiltIn",
        "mode": "Indexed",
        "parameters": {
          "tagName": {
            "type": "String",
            "defaultValue": "CostCenter"
          }
        },
        "policyRule": {
          "if": {
            "not": {
              "field": "[concat('tags[', parameters('tagName'), ']')]",
              "exists": "true"
            }
          },
          "then": {
            "effect": "deny"
          }
        }
      }
    ]
  }
}

GCP Labels

resource "google_compute_instance" "example" {
  name         = "example-instance"
  machine_type = "e2-micro"
  zone         = "us-east1-a"
  
  labels = {
    environment = var.environment
    team        = var.team
    cost_center = var.cost_center
    project     = var.project
  }
  
  # Force labels on child resources
  metadata = {
    enable-oslogin = true
  }
}

AWS Cost Explorer Integration

#!/usr/bin/env python3
"""AWS Cost Explorer API for detailed cost analysis."""

import boto3
from datetime import datetime, timedelta

ce = boto3.client('ce')

def get_cost_by_service(start_date, end_date):
    """Get cost breakdown by service."""
    
    response = ce.get_cost_and_usage(
        TimePeriod={
            'Start': start_date,
            'End': end_date
        },
        Granularity='MONTHLY',
        Metrics=['UnblendedCost', 'BlendedCost'],
        GroupBy=[
            {'Type': 'DIMENSION', 'Key': 'SERVICE'}
        ]
    )
    
    return response['ResultsByTime']

def get_cost_by_tag(start_date, end_date, tag_key):
    """Get cost breakdown by tag."""
    
    response = ce.get_cost_and_usage(
        TimePeriod={
            'Start': start_date,
            'End': end_date
        },
        Granularity='MONTHLY',
        Metrics=['UnblendedCost'],
        GroupBy=[
            {'Type': 'TAG', 'Key': tag_key}
        ]
    )
    
    return response['ResultsByTime']

def get_cost_by_linked_account(start_date, end_date):
    """Get cost by linked account."""
    
    response = ce.get_cost_and_usage(
        TimePeriod={
            'Start': start_date,
            'End': end_date
        },
        Granularity='MONTHLY',
        Metrics=['UnblendedCost'],
        GroupBy=[
            {'Type': 'DIMENSION', 'Key': 'LINKED_ACCOUNT'}
        ]
    )
    
    return response['ResultsByTime']

def generate_monthly_report():
    """Generate comprehensive monthly cost report."""
    
    end_date = datetime.now()
    start_date = (end_date - timedelta(days=30)).strftime('%Y-%m-%d')
    end_date_str = end_date.strftime('%Y-%m-%d')
    
    report = {
        'period': f"{start_date} to {end_date_str}",
        'generated': datetime.now().isoformat(),
        'by_service': get_cost_by_service(start_date, end_date_str),
        'by_environment': get_cost_by_tag(start_date, end_date_str, 'Environment'),
        'by_team': get_cost_by_tag(start_date, end_date_str, 'Team'),
        'by_account': get_cost_by_linked_account(start_date, end_date_str)
    }
    
    return report

if __name__ == '__main__':
    report = generate_monthly_report()
    print(f"Monthly Cost Report: {report['period']}")

Chargeback Model

Monthly Chargeback Report

#!/usr/bin/env python3
"""Generate chargeback reports for teams."""

import boto3
import json
from datetime import datetime
from collections import defaultdict

ce = boto3.client('ce')
organizations = boto3.client('organizations')

def get_all_accounts():
    """Get all AWS accounts in organization."""
    
    accounts = organizations.list_accounts()['Accounts']
    return {acc['Id']: acc['Name'] for acc in accounts}

def calculate_team_costs(start_date, end_date, tag_key):
    """Calculate costs by team tag."""
    
    response = ce.get_cost_and_usage(
        TimePeriod={
            'Start': start_date,
            'End': end_date
        },
        Granularity='MONTHLY',
        Metrics=['UnblendedCost', 'UsageQuantity'],
        GroupBy=[
            {'Type': 'TAG', 'Key': tag_key}
        ]
    )
    
    costs = defaultdict(lambda: {'total': 0, 'services': defaultdict(float)})
    
    for period in response['ResultsByTime']:
        for group in period['Groups']:
            team = group['Keys'][0].replace(f"{tag_key}$", "")
            if team == "not tagged":
                continue
                
            amount = float(group['Metrics']['UnblendedCost']['Amount'])
            costs[team]['total'] += amount
    
    return dict(costs)

def generate_chargeback_report():
    """Generate chargeback report for the month."""
    
    end_date = datetime.now()
    start_date = (end_date.replace(day=1) - timedelta(days=1)).strftime('%Y-%m-01')
    last_day = end_date.strftime('%Y-%m-%d')
    
    accounts = get_all_accounts()
    
    report = {
        'billing_period': f"{start_date} to {last_day}",
        'generated': datetime.now().isoformat(),
        'total_cost': 0,
        'teams': []
    }
    
    # Get costs by team
    team_costs = calculate_team_costs(start_date, last_day, 'Team')
    
    for team, data in team_costs.items():
        team_entry = {
            'team': team,
            'total_cost': round(data['total'], 2),
            'currency': 'USD'
        }
        report['teams'].append(team_entry)
        report['total_cost'] += data['total']
    
    # Sort by cost descending
    report['teams'].sort(key=lambda x: x['total_cost'], reverse=True)
    
    return report

if __name__ == '__main__':
    report = generate_chargeback_report()
    print(json.dumps(report, indent=2))

Showback Dashboard

# Grafana Dashboard for Showback
{
  "dashboard": {
    "title": "Cost Showback - Team View",
    "tags": ["finops", "showback"],
    "timezone": "browser",
    "panels": [
      {
        "title": "Cost by Team",
        "type": "bargauge",
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 0},
        "targets": [
          {
            "expr": "sum by (team) (AWS_Cost_By_Tag{tag=\"Team\"})",
            "legendFormat": "{{team}}"
          }
        ]
      },
      {
        "title": "Cost Trend by Environment",
        "type": "timeseries",
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 0},
        "targets": [
          {
            "expr": "sum by (environment) (AWS_Cost_By_Tag{tag=\"Environment\"})",
            "legendFormat": "{{environment}}"
          }
        ]
      },
      {
        "title": "Cost by Service",
        "type": "piechart",
        "gridPos": {"h": 8, "w": 8, "x": 0, "y": 8},
        "targets": [
          {
            "expr": "topk(10, sum by (service) (AWS_Cost_By_Service))",
            "legendFormat": "{{service}}"
          }
        ]
      }
    ]
  }
}

Budget Alerts

# Terraform AWS Budget
resource "aws_budgets_budget" "monthly" {
  name              = "monthly-budget"
  budget_type       = "COST"
  limit_amount      = "10000"
  limit_unit        = "USD"
  time_period_start = "2026-01-01"
  time_unit         = "MONTHLY"
  
  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                   = 80
    threshold_type              = "PERCENTAGE"
    notification_type           = "FORECASTED"
    subscriber_email_addresses  = ["[email protected]"]
  }
  
  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                   = 100
    threshold_type              = "PERCENTAGE"
    notification_type           = "ACTUAL"
    subscriber_email_addresses  = ["[email protected]", "[email protected]"]
  }
}
# Team-specific budgets
resource "aws_budgets_budget" "team_budget" {
  count = length(var.teams)
  
  name              = "${var.teams[count.index]}-monthly-budget"
  budget_type       = "COST"
  limit_amount      = var.team_budgets[count.index]
  limit_unit        = "USD"
  time_period_start = "2026-01-01"
  time_unit         = "MONTHLY"
  
  cost_filters {
    tag_key   = "Team"
    tag_value = var.teams[count.index]
  }
  
  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                   = 90
    threshold_type              = "PERCENTAGE"
    notification_type           = "ACTUAL"
    subscriber_email_addresses  = ["${var.teams[count.index]}[email protected]"]
  }
}

FinOps Tools Comparison

Tool Provider Features Cost
AWS Cost Explorer AWS Native, Basic Free
AWS CUR AWS Detailed analytics ~$0.01/GB
Azure Cost Management Azure Native Free
GCP Cloud Billing GCP Native Free
Kubecost Multi K8s focused Free/$
CloudHealth VMware Multi-cloud $$$
Cloudability SAP Enterprise $$$$
Spot.io Spot Optimization Free/$

Cost Anomaly Detection

#!/usr/bin/env python3
"""Cost anomaly detection using statistical analysis."""

import boto3
import numpy as np
from datetime import datetime, timedelta

ce = boto3.client('ce')

def detect_anomalies():
    """Detect cost anomalies using statistical analysis."""
    
    # Get 90 days of historical data
    end_date = datetime.now()
    dates = []
    costs = []
    
    for i in range(90):
        date = (end_date - timedelta(days=90-i)).strftime('%Y-%m-%d')
        response = ce.get_cost_and_usage(
            TimePeriod={'Start': date, 'End': (end_date - timedelta(days=89-i)).strftime('%Y-%m-%d')},
            Granularity='DAILY',
            Metrics=['UnblendedCost']
        )
        daily_cost = float(response['ResultsByTime'][0]['Total']['UnblendedCost']['Amount'])
        dates.append(date)
        costs.append(daily_cost)
    
    # Calculate statistical thresholds
    mean_cost = np.mean(costs)
    std_cost = np.std(costs)
    
    # Flag anomalies (>2 std deviations)
    anomalies = []
    for i, cost in enumerate(costs):
        z_score = (cost - mean_cost) / std_cost
        if abs(z_score) > 2:
            anomalies.append({
                'date': dates[i],
                'cost': cost,
                'z_score': z_score,
                'percent_change': ((cost - mean_cost) / mean_cost) * 100
            })
    
    return {
        'mean_cost': mean_cost,
        'std_cost': std_cost,
        'anomalies': anomalies
    }

if __name__ == '__main__':
    result = detect_anomalies()
    print(f"Mean: ${result['mean_cost']:.2f}, Std: ${result['std_cost']:.2f}")
    print(f"Anomalies: {result['anomalies']}")

External Resources


Comments