Skip to content
Back to Blog
1 min read

FinOps Adoption: Managing Azure Costs Effectively

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

Data & AI Director and Microsoft MVP based in Sydney. Writing about Microsoft Fabric, Azure, data engineering, and AI.