Back to Blog
5 min read

FinOps Adoption: Managing Azure Costs Effectively

FinOps - the practice of bringing financial accountability to cloud spending - matured significantly in 2022. Let’s explore how to implement effective cloud cost management in Azure.

FinOps Principles

  1. Teams need to collaborate: Finance, engineering, and business work together
  2. Everyone takes ownership: Cost is everyone’s responsibility
  3. A centralized team drives FinOps: But doesn’t own all costs
  4. Reports should be accessible and timely: Real-time visibility
  5. Decisions are driven by business value: Not just cost reduction
  6. Take advantage of variable costs: Cloud’s pay-as-you-go model

Azure Cost Management Setup

// Enable cost management and budgets
resource budget 'Microsoft.Consumption/budgets@2021-10-01' = {
  name: 'monthly-budget'
  properties: {
    category: 'Cost'
    amount: 10000
    timeGrain: 'Monthly'
    timePeriod: {
      startDate: '2022-12-01'
      endDate: '2023-12-31'
    }
    filter: {
      dimensions: {
        name: 'ResourceGroup'
        values: [
          'rg-production'
          'rg-staging'
        ]
      }
    }
    notifications: {
      Actual_GreaterThan_80_Percent: {
        enabled: true
        operator: 'GreaterThan'
        threshold: 80
        contactEmails: [
          'finops@company.com'
          'engineering-leads@company.com'
        ]
        thresholdType: 'Actual'
      }
      Forecasted_GreaterThan_100_Percent: {
        enabled: true
        operator: 'GreaterThan'
        threshold: 100
        contactEmails: [
          'finops@company.com'
          'cto@company.com'
        ]
        thresholdType: 'Forecasted'
      }
    }
  }
}

Cost Allocation with Tags

// Enforce tagging policy
resource tagPolicy 'Microsoft.Authorization/policyAssignments@2021-06-01' = {
  name: 'require-cost-tags'
  properties: {
    policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/1e30110a-5ceb-460c-a204-c1c3969c6d62'
    parameters: {
      tagName1: { value: 'CostCenter' }
      tagName2: { value: 'Environment' }
      tagName3: { value: 'Owner' }
      tagName4: { value: 'Application' }
    }
  }
}

// Tag inheritance for resources
resource tagInheritance 'Microsoft.Authorization/policyAssignments@2021-06-01' = {
  name: 'inherit-rg-tags'
  properties: {
    policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/cd3aa116-8754-49c9-a813-ad46512ece54'
    parameters: {
      tagName: { value: 'CostCenter' }
    }
  }
}

Cost Analysis Automation

from azure.identity import DefaultAzureCredential
from azure.mgmt.costmanagement import CostManagementClient
from datetime import datetime, timedelta
import pandas as pd

class AzureCostAnalyzer:
    def __init__(self, subscription_id: str):
        credential = DefaultAzureCredential()
        self.client = CostManagementClient(credential)
        self.scope = f"/subscriptions/{subscription_id}"

    def get_costs_by_service(self, days: int = 30) -> pd.DataFrame:
        """Get costs grouped by service."""
        end_date = datetime.utcnow()
        start_date = end_date - timedelta(days=days)

        query = {
            "type": "ActualCost",
            "timeframe": "Custom",
            "timePeriod": {
                "from": start_date.strftime("%Y-%m-%d"),
                "to": end_date.strftime("%Y-%m-%d")
            },
            "dataset": {
                "granularity": "None",
                "aggregation": {
                    "totalCost": {"name": "Cost", "function": "Sum"}
                },
                "grouping": [
                    {"type": "Dimension", "name": "ServiceName"}
                ]
            }
        }

        result = self.client.query.usage(self.scope, query)

        return pd.DataFrame(
            result.rows,
            columns=["Service", "Cost", "Currency"]
        ).sort_values("Cost", ascending=False)

    def get_costs_by_team(self, days: int = 30) -> pd.DataFrame:
        """Get costs grouped by team (using CostCenter tag)."""
        end_date = datetime.utcnow()
        start_date = end_date - timedelta(days=days)

        query = {
            "type": "ActualCost",
            "timeframe": "Custom",
            "timePeriod": {
                "from": start_date.strftime("%Y-%m-%d"),
                "to": end_date.strftime("%Y-%m-%d")
            },
            "dataset": {
                "granularity": "None",
                "aggregation": {
                    "totalCost": {"name": "Cost", "function": "Sum"}
                },
                "grouping": [
                    {"type": "Tag", "name": "CostCenter"}
                ]
            }
        }

        result = self.client.query.usage(self.scope, query)

        return pd.DataFrame(
            result.rows,
            columns=["CostCenter", "Cost", "Currency"]
        ).sort_values("Cost", ascending=False)

    def identify_waste(self) -> dict:
        """Identify potential cost optimization opportunities."""
        # This would integrate with Azure Advisor
        # For now, return placeholder structure
        return {
            "idle_resources": [],
            "oversized_vms": [],
            "unattached_disks": [],
            "unused_public_ips": [],
            "potential_savings": 0
        }

Cost Optimization Strategies

Right-Sizing

from azure.mgmt.compute import ComputeManagementClient
from azure.mgmt.monitor import MonitorManagementClient

def analyze_vm_utilization(subscription_id: str, resource_group: str):
    """Analyze VM CPU/Memory utilization for right-sizing."""

    credential = DefaultAzureCredential()
    compute_client = ComputeManagementClient(credential, subscription_id)
    monitor_client = MonitorManagementClient(credential, subscription_id)

    recommendations = []

    for vm in compute_client.virtual_machines.list(resource_group):
        # Get CPU metrics for last 7 days
        resource_id = vm.id
        metrics = monitor_client.metrics.list(
            resource_id,
            metricnames="Percentage CPU",
            aggregation="Average",
            timespan="P7D",
            interval="PT1H"
        )

        avg_cpu = calculate_average(metrics)

        if avg_cpu < 10:
            recommendations.append({
                "vm_name": vm.name,
                "current_size": vm.hardware_profile.vm_size,
                "avg_cpu": avg_cpu,
                "recommendation": "Consider deallocating or downsizing",
                "potential_savings": estimate_savings(vm)
            })
        elif avg_cpu < 30:
            recommendations.append({
                "vm_name": vm.name,
                "current_size": vm.hardware_profile.vm_size,
                "avg_cpu": avg_cpu,
                "recommendation": "Consider downsizing",
                "potential_savings": estimate_downsize_savings(vm)
            })

    return recommendations

Reserved Instances

def analyze_reservation_opportunities(costs_by_service: pd.DataFrame) -> dict:
    """Identify candidates for reserved instances."""

    # Services that benefit from reservations
    reservable_services = [
        "Virtual Machines",
        "Azure Cosmos DB",
        "SQL Database",
        "Azure Synapse Analytics",
        "Azure Cache for Redis"
    ]

    recommendations = []

    for _, row in costs_by_service.iterrows():
        if row["Service"] in reservable_services:
            monthly_cost = row["Cost"]

            # 1-year reservation typically saves ~20%
            # 3-year reservation typically saves ~40%
            recommendations.append({
                "service": row["Service"],
                "monthly_cost": monthly_cost,
                "1_year_savings": monthly_cost * 0.20 * 12,
                "3_year_savings": monthly_cost * 0.40 * 36,
                "recommendation": "Evaluate reservation purchase"
            })

    return {
        "recommendations": recommendations,
        "total_1_year_savings": sum(r["1_year_savings"] for r in recommendations),
        "total_3_year_savings": sum(r["3_year_savings"] for r in recommendations)
    }

FinOps Dashboard

# Streamlit dashboard for FinOps
import streamlit as st
import plotly.express as px

def create_finops_dashboard():
    st.title("Azure FinOps Dashboard")

    # Cost trend
    st.header("Cost Trend")
    costs = get_daily_costs(30)
    fig = px.line(costs, x="date", y="cost", title="Daily Costs (Last 30 Days)")
    st.plotly_chart(fig)

    # Cost by service
    st.header("Cost by Service")
    by_service = get_costs_by_service()
    fig = px.pie(by_service, values="Cost", names="Service")
    st.plotly_chart(fig)

    # Cost by team
    st.header("Cost by Team")
    by_team = get_costs_by_team()
    fig = px.bar(by_team, x="CostCenter", y="Cost")
    st.plotly_chart(fig)

    # Optimization opportunities
    st.header("Optimization Opportunities")
    opportunities = get_optimization_recommendations()

    for opp in opportunities:
        st.write(f"**{opp['resource']}**: {opp['recommendation']}")
        st.write(f"Potential savings: ${opp['savings']:,.2f}/month")

    # Budget status
    st.header("Budget Status")
    budget = get_budget_status()
    progress = budget["actual"] / budget["limit"]
    st.progress(progress)
    st.write(f"${budget['actual']:,.2f} / ${budget['limit']:,.2f} ({progress*100:.1f}%)")

Team Accountability

# FinOps operating model
operating_model:
  central_team:
    name: Cloud FinOps Team
    responsibilities:
      - Set policies and standards
      - Provide tools and dashboards
      - Negotiate enterprise agreements
      - Identify cross-cutting optimizations
      - Train teams on FinOps practices

  application_teams:
    responsibilities:
      - Own their application costs
      - Implement tagging standards
      - Right-size resources
      - Clean up unused resources
      - Budget planning

  review_cadence:
    weekly:
      - Anomaly review
      - Budget tracking
    monthly:
      - Team cost reviews
      - Optimization progress
    quarterly:
      - Reservation planning
      - Architecture reviews
      - Budget adjustments

Conclusion

FinOps is about culture change as much as tooling. Success requires collaboration between finance, engineering, and leadership. Azure provides excellent native tools through Cost Management - the key is using them consistently and making cost visibility part of daily operations.

Resources

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.