Skip to content

Billing Hierarchy: Cost Attribution & Budget Management

Billing Fundamentals

Organization
├── Projects (tạo resources)
└── Billing Accounts (track costs)

Multiple projects → 1 billing account
1 project → Can link to 1 billing account

Key relationship:

  • Project: Where resources run (compute, storage, etc.)
  • Billing Account: Receives invoice, pays for all linked projects

Billing Account Structure

bash
# List billing accounts
gcloud billing accounts list

# Link project to billing account
gcloud billing projects link PROJECT_ID \
  --billing-account=BILLING_ACCOUNT_ID

# See which billing account project is linked to
gcloud billing projects describe PROJECT_ID

Cost Attribution Models

Model 1: Per-Project Costs

Simplest: Each project = separate cost center

project-prod: $10,000/month
project-staging: $2,000/month
project-dev: $500/month

Limitation: Cannot attribute costs within project (e.g., which service costs most?)

Model 2: Per-Label Costs

More granular: Use labels for cost allocation

python
# Query costs by label
from google.cloud import bigquery

query = """
SELECT
  labels.key,
  labels.value,
  SUM(cost) as total_cost
FROM `project.billing.gcp_billing_export_v1_XXXXXX`
WHERE DATE(usage_start_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY labels.key, labels.value
HAVING SUM(cost) > 100  -- Only show costs > $100
ORDER BY total_cost DESC
"""

results = bigquery.Client().query(query).result()
for row in results:
    print(f"{row.key}={row.value}: ${row.total_cost:.2f}")

Model 3: Chargeback

Charge teams for their resource usage:

python
def calculate_chargeback():
    """Monthly chargeback per cost-center"""
    
    # Get costs by cost-center label
    costs_by_center = query_costs_by_label("cost-center")
    
    charges = {}
    for cost_center, cost in costs_by_center.items():
        team = get_team_for_cost_center(cost_center)
        
        # Calculate departmental overhead (e.g., 5%)
        overhead = cost * 0.05
        total_charge = cost + overhead
        
        charges[team] = {
            "direct_cost": cost,
            "overhead": overhead,
            "total": total_charge
        }
    
    # Send invoices
    for team, charges_detail in charges.items():
        send_email(
            to=f"finance-{team}@company.com",
            subject=f"GCP Chargeback - {team}",
            body=f"""
            Direct costs: ${charges_detail['direct_cost']:.2f}
            Overhead (5%): ${charges_detail['overhead']:.2f}
            Total: ${charges_detail['total']:.2f}
            """
        )

Budget Alerts

Alert on budget exceeded

bash
# Create budget alert
gcloud billing budgets create \
  --billing-account=BILLING_ACCOUNT_ID \
  --display-name="Monthly budget limit" \
  --budget-amount=5000 \
  --threshold-rule=percent=50,spend_basis=current_spend \
  --threshold-rule=percent=90,spend_basis=current_spend \
  --threshold-rule=percent=100,spend_basis=current_spend

Custom budget alerting via Pub/Sub

python
from google.cloud import pubsub_v1, monitoring_v3

def create_budget_alert():
    """Alert via Pub/Sub when budget exceeded"""
    
    # Create budget
    service = billingbudgets.BudgetServiceClient()
    
    budget = billingbudgets.Budget(
        display_name="Q1 2024 Budget",
        budget_amount=billingbudgets.BudgetAmount(
            specified_amount={"currency_code": "USD", "units": 10000}
        ),
        threshold_rules=[
            billingbudgets.ThresholdRule(percent_spend_basis=90),
            billingbudgets.ThresholdRule(percent_spend_basis=100)
        ],
        notifications_rule=billingbudgets.NotificationsRule(
            pubsub_topic=f"projects/PROJECT_ID/topics/budget-alerts",
            schema_version="1.0"
        )
    )
    
    service.create_budget(
        parent=f"billingAccounts/{BILLING_ACCOUNT_ID}",
        budget=budget
    )

Cost Optimization

Identify expensive resources

sql
-- Find top 10 most expensive resources
SELECT
  resource.name,
  resource.labels.value as resource_label,
  SUM(cost) as total_cost,
  COUNT(*) as line_items
FROM `project.billing.gcp_billing_export_v1_XXXXXX`
WHERE DATE(usage_start_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY resource.name, resource.labels.value
ORDER BY total_cost DESC
LIMIT 10;

-- Find unused resources
SELECT
  resource.name,
  COUNT(*) as usage_count
FROM `project.billing.gcp_billing_export_v1_XXXXXX`
WHERE DATE(usage_start_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY)
GROUP BY resource.name
HAVING COUNT(*) < 10  -- Very low usage
ORDER BY usage_count ASC;

Reservation discounts

bash
# Get discount for reserved capacity (25-55% off on-demand)

# Compute Engine reservations
gcloud compute reservations create prod-vms \
  --project=PROJECT_ID \
  --zone=us-central1-a \
  --vm-family=N1 \
  --local-ssd=0 \
  --accelerator-type=nvidia-tesla-k80 \
  --accelerator-count=2 \
  --vm-count=10

# BigQuery slots (annual commitment)
gcloud compute reservations create bigquery-annual \
  --annual-commitment

Multi-Project Billing

Consolidated billing account

Organization
├── Prod Project
│   ├── VM: $5,000
│   └── Database: $3,000
├── Staging Project
│   └── VM: $500
└── Billing Account
    ├── Month 1: $8,500
    ├── Month 2: $9,200
    └── Consolidated invoice

Benefits:

  • Single invoice
  • Shared discounts (volume discounts across projects)
  • Centralized payment
  • Easier reconciliation

Terraform Billing Management

hcl
# Terraform: Budget alert + chargeback automation

resource "google_billing_budget" "monthly_budget" {
  billing_account = var.billing_account_id
  display_name    = "Monthly team budget"

  budget_amount {
    specified_amount {
      currency_code = "USD"
      units         = "50000"
    }
  }

  threshold_rules {
    percent_spend_basis = 50
  }

  threshold_rules {
    percent_spend_basis = 90
  }

  threshold_rules {
    percent_spend_basis = 100
  }

  notifications_rule {
    pubsub_topic           = google_pubsub_topic.budget_alerts.id
    schema_version         = "1.0"
    monitoring_notification_channels = [
      google_monitoring_notification_channel.email.id
    ]
  }
}

# Cloud Function: Triggered by budget alert
resource "google_cloudfunctions_function" "chargeback" {
  name        = "monthly-chargeback"
  runtime     = "python39"
  entry_point = "process_budget_alert"

  source_archive_bucket = google_storage_bucket.functions.name
  source_archive_object = google_storage_bucket_object.chargeback_zip.name

  event_trigger {
    event_type = "google.pubsub.topic.publish"
    resource   = google_pubsub_topic.budget_alerts.id
  }
}

Anti-Patterns

Anti-patternProblemSolution
One billing account per projectCannot see total org spendingConsolidated billing account
No cost tracking labelsCannot attribute costsMandatory labels
No budget alertsOverspending invisibleSet alerts at key thresholds
No chargebackTeams waste resourcesImplement cost allocation
Manual invoicingErrors, delaysAutomated chargeback
No cost optimizationWaste money on unused resourcesRegular cost audits

References