Skip to main content
  1. Posts/

Terraforming Azure 2.

·14 mins·

Continuing from the previous post, here is the next part of the series.

Cost considerations #

Before we start to madly creating resources, let’s take a look at the cost.

money

In my case I have decided to manage budgeting and cost viewing via Terraform as well. Admittedly, during the process there were a few snags, and I was close to giving it up, but eventually I got to a state with which I and managemed are mutually satisfied.

A little inspiration #

I will be honest, writing a module that handles the consumption reports from scratch was something I started - and got to relatively working state on budgets…on cost views a little less so - but eventually I shamelesly took “inspiration” from this repository. So big thank you for the Gravicore Team! (not related / know the company by the way)

The only change that I did, is ensuring we use up-to-date providers, as the modules seem to be a bit outdated.

versions

Use the modules now #

Right, so now that I have this module not too far from the deploymnent, and configured providers and backend (see previous post), I also added a module to help me with conforming Azure naming conventions:

module "naming" {
  source = "Azure/naming/azurerm"
}

What this module does is sufficiently explained here

Variables #

The variables I have created when using the module are as follows:

#------------------------------------------------------------------------------
# Azure Subscription IDs
# These variables define the subscription IDs for different
# parts of the BCMG platform architecture
#------------------------------------------------------------------------------
variable "platform_con_subscription_id" {
  type        = string
  description = "Subscription ID for BCMG Platform Core Connectivity resources"
  default     = "00000000-0000-0000-0000-000000000000"
}

variable "platform_mgmt_subscription_id" {
  type        = string
  description = "Subscription ID for BCMG Platform Management resources"
  default     = "00000000-0000-0000-0000-000000000000"
}

variable "platform_id_subscription_id" {
  type        = string
  description = "Subscription ID for BCMG Platform Identity resources"
  default     = "00000000-0000-0000-0000-000000000000"
}

variable "landingzone_Empirics_subscription_id" {
  type        = string
  description = "Subscription ID for BCMG Empirics landing zone resources"
  default     = "00000000-0000-0000-0000-000000000000"
}

#------------------------------------------------------------------------------
# Location and Environment Settings
# These variables control where resources are deployed and
# in which environment context
#------------------------------------------------------------------------------
variable "location" {
  type        = string
  description = "Primary Azure region where resources will be deployed"
  default     = "northeurope"
}

variable "location_short" {
  type        = string
  description = "Short code for Azure region, used in resource naming conventions"
  default     = "ne"
}

variable "environment" {
  type        = string
  description = "Deployment environment (dev, test, prod, etc.) - affects resource configuration and naming"
  default     = "prod"
}

#------------------------------------------------------------------------------
# Resource Tagging
# Standardized tags applied to all resources for governance,
# cost tracking, and resource organization
#------------------------------------------------------------------------------
variable "tags" {
  type        = map(string)
  description = "Map of tags to be applied to all resources created by this configuration"
  default = {
    DomainName = "bcmg.com"
    Domain     = "BCMG"
    ManagedBy  = "Terraform"
  }
}

#------------------------------------------------------------------------------
# Cost Management Settings
# Configuration for Azure Cost Management alerts and reports
#------------------------------------------------------------------------------
variable "cost_management_recipient_emails" {
  type        = list(string)
  description = "List of email addresses to receive Azure cost management notifications and reports"
  default     = ["admin@example.com", "finance@example.com"]
}

variable "cost_management_sender_email" {
  type        = string
  description = "Email address used as the sender for Azure cost management notifications"
  default     = "azure-noreply@example.com"
}

variable "cost_management_provider" {
  type        = string
  description = "Name of the Cost Management resource provider to register - refer to https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-services-resource-providers for other providers"
  default     = "Microsoft.CostManagementExports"
}

#------------------------------------------------------------------------------
# Budget Configuration
# Settings for consumption budgets across subscriptions
#------------------------------------------------------------------------------
variable "budget_amount" {
  type        = number
  description = "Default budget amount in currency of the billing account"
  default     = 200
}

variable "budget_time_period_start" {
  type        = string
  description = "Start date for budget time period in ISO 8601 format"
  default     = "2025-03-01T00:00:00Z"
}

variable "budget_time_period_end" {
  type        = string
  description = "End date for budget time period in ISO 8601 format"
  default     = "2026-03-01T00:00:00Z"
}

#------------------------------------------------------------------------------
# Budget Notification Thresholds
# Threshold settings for budget alerts
#------------------------------------------------------------------------------
variable "budget_actual_threshold" {
  type        = number
  description = "Threshold percentage for actual budget consumption alerts"
  default     = 80
}

variable "budget_forecast_threshold" {
  type        = number
  description = "Threshold percentage for forecasted budget consumption alerts"
  default     = 90
}

#------------------------------------------------------------------------------
# Cost Management Report Configuration
# Settings for cost management reports and scheduling
#------------------------------------------------------------------------------
variable "report_chart_type" {
  type        = string
  description = "Chart type for cost management views"
  default     = "StackedColumn"
}

variable "report_granularity" {
  type        = string
  description = "Data granularity for cost management reports"
  default     = "Daily"
}

variable "report_weekly_schedule" {
  type        = list(string)
  description = "Days of the week for weekly cost reports"
  default     = ["Monday", "Friday"]
}

variable "report_weekly_hour" {
  type        = number
  description = "Hour of the day to send weekly reports (0-23)"
  default     = 8
}

variable "report_monthly_hour" {
  type        = number
  description = "Hour of the day to send monthly reports (0-23)"
  default     = 23
}

variable "report_monthly_day" {
  type        = number
  description = "Day of the month to send monthly reports (1-31)"
  default     = 30
}

#------------------------------------------------------------------------------
# Resource Group Configuration
# Resource group names for different workloads
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Resource Group Configuration
# Resource group names for different workloads
#------------------------------------------------------------------------------
variable "resource_group_names" {
  type        = map(string)
  description = "Map of resource group names for different workloads"
  default = {
    connectivity = "rg-connectivity",
    identity     = "rg-identity",
    management   = "rg-management",
    empirics     = "rg-empirics"
  }
}

#------------------------------------------------------------------------------
# Cost Management Export Configuration
# Settings for export storage and export jobs
#------------------------------------------------------------------------------
variable "export_storage_account_tier" {
  type        = string
  description = "Storage account tier for cost management exports"
  default     = "Standard"
}

variable "export_storage_replication_type" {
  type        = string
  description = "Storage replication type for cost management exports"
  default     = "LRS"
}

variable "export_container_access_type" {
  type        = string
  description = "Access type for the export storage container"
  default     = "container"
}

variable "export_recurrence_type" {
  type        = string
  description = "Recurrence type for cost management exports"
  default     = "Monthly"
}

#? note: https://learn.microsoft.com/en-us/azure/cost-management-billing/cost-management-billing-faq#when-does-azure-finalize-or-close-the-billing-cycle-of-a-closed-month
variable "export_start_date" {
  type        = string
  description = "Exact start date for cost management exports in format YYYY-MM-DDT00:00:00Z. If provided, this takes precedence over export_start_offset."
  default     = ""
}

#? note: https://learn.microsoft.com/en-us/azure/cost-management-billing/cost-management-billing-faq#when-does-azure-finalize-or-close-the-billing-cycle-of-a-closed-month
variable "export_end_date" {
  type        = string
  description = "Exact end date for cost management exports in format YYYY-MM-DDT00:00:00Z. If provided, this takes precedence over export_end_offset."
  default     = ""
}

variable "export_start_offset" {
  type        = string
  description = "Time offset for export period start date (e.g., '24h' for tomorrow)"
  default     = "24h"
}

variable "export_end_offset" {
  type        = string
  description = "Time offset for export period end date (e.g., '8760h' for one year)"
  default     = "8760h"
}

variable "export_file_format" {
  type        = string
  description = "File format for cost management exports"
  default     = "Csv"
}

variable "export_data_type" {
  type        = string
  description = "Data type for cost management exports"
  default     = "ActualCost"
}

variable "export_time_frame" {
  type        = string
  description = "Time frame for cost management exports"
  default     = "TheLastBillingMonth"
}

While some has default values, others are set to empty strings. This is because the values will be set by the terraform.tfvars file. (In the repository there is a template that is meant to be copied, filled and renamed to terraform.tfvars.)

Deployment code #

Now for the deployment currently I have two files:

  • this one to create the neccesary exports:
/* -------------------------------------------------------------------------- */
/*                           Subsciptions to process                          */
/* -------------------------------------------------------------------------- */
locals {
  subscriptions = {
    management = {
      id = "/subscriptions/${var.platform_mgmt_subscription_id}"
    }
    connectivity = {
      id = "/subscriptions/${var.platform_con_subscription_id}"
    }
    identity = {
      id = "/subscriptions/${var.platform_id_subscription_id}"
    }
    empirics = {
      id = "/subscriptions/${var.landingzone_Empirics_subscription_id}"
    }
  }
}

/* -------------------------------------------------------------------------- */
/*                                 Export storage                             */
/* -------------------------------------------------------------------------- */

resource "azurerm_storage_account" "export" {
  provider = azurerm.management

  name                = replace("${module.naming.storage_account.name}-exports-00-all-${var.environment}-${var.location_short}", "-", "")
  resource_group_name = local.cost_consumption_configs.management.resource_group_name

  location                 = var.location
  account_tier             = var.export_storage_account_tier
  account_replication_type = var.export_storage_replication_type
  tags                     = var.tags
}

resource "azurerm_storage_container" "export" {
  provider = azurerm.management

  name               = "${module.naming.storage_container.name}-exports-00-all-${var.environment}-${var.location_short}"
  storage_account_id = azurerm_storage_account.export.id

  container_access_type = var.export_container_access_type
  # Note: Storage containers don't support tags
}

/* -------------------------------------------------------------------------- */
/*                           Neccesary registration                           */
/* -------------------------------------------------------------------------- */

/* ----------------------------- Cost Management ---------------------------- */
resource "azurerm_resource_provider_registration" "management" {
  provider = azurerm.management
  name     = var.cost_management_provider
  # Note: Resource provider registrations don't support tags
}
resource "azurerm_resource_provider_registration" "connectivity" {
  provider = azurerm.connectivity
  name     = var.cost_management_provider
}
resource "azurerm_resource_provider_registration" "identity" {
  provider = azurerm.identity
  name     = var.cost_management_provider
}
resource "azurerm_resource_provider_registration" "empirics" {
  provider = azurerm.lz_empirics
  name     = var.cost_management_provider
}

/* -------------------------------------------------------------------------- */
/*                             Export definitions                             */
/* -------------------------------------------------------------------------- */

locals {
  # Determine start date based on whether an exact date is provided or an offset should be used
  export_start_date = var.export_start_date != "" ? var.export_start_date : "${formatdate("YYYY-MM-DD", timeadd(timestamp(), var.export_start_offset))}T00:00:00Z"

  # Determine end date based on whether an exact date is provided or an offset should be used
  export_end_date = var.export_end_date != "" ? var.export_end_date : "${formatdate("YYYY-MM-DD", timeadd(timestamp(), var.export_end_offset))}T00:00:00Z"
}

/* ----------------------------------------- Use `az costmanagement export list --scope "/subscriptions/< subscription ID >"` to verify if export is created ---------------------------------------- */

resource "azurerm_subscription_cost_management_export" "all_subscriptions" {
  for_each = local.subscriptions

  provider = azurerm.management

  name            = "cost-export-${each.key}-${var.environment}-${var.location_short}"
  subscription_id = each.value.id
  recurrence_type = var.export_recurrence_type

  recurrence_period_start_date = local.export_start_date
  recurrence_period_end_date   = local.export_end_date

  file_format = var.export_file_format

  export_data_storage_location {
    container_id     = azurerm_storage_container.export.id
    root_folder_path = "/root/${each.key}/"
  }

  export_data_options {
    type       = var.export_data_type
    time_frame = var.export_time_frame
  }

  depends_on = [
    azurerm_resource_provider_registration.connectivity,
    azurerm_resource_provider_registration.identity,
    azurerm_resource_provider_registration.empirics,
    azurerm_resource_provider_registration.management
  ]

  lifecycle {
    ignore_changes = [
      #! if you want to modify these, `taint` the resource
      export_data_storage_location.0.root_folder_path
      # We're no longer ignoring date changes since they're now dynamic
    ]
  }
}
  • and this one to create the reports and budgets
##? Notable: cost reports must be scheduled at least one day ahead (won't start the same day!)
##? https://learn.microsoft.com/en-us/azure/cost-management-billing/costs/tutorial-export-acm-data?tabs=azure-portal#export-schedule

/* -------------------------------------------------------------------------- */
/*                                   Configurations                           */
/* -------------------------------------------------------------------------- */
locals {
  cost_consumption_configs = {
    connectivity = {
      name                = "conn-consumption"
      resource_group_name = var.resource_group_names.connectivity

      /* -------------------------------------------------------------------------- */
      /*                Subscription consumption budget configuration               */
      /* -------------------------------------------------------------------------- */
      subscription_consumption_budget = {
        "cbs-connectivity-01-conn-${var.environment}-${var.location_short}" = {
          name   = "cbs-connectivity-01-conn-${var.environment}-${var.location_short}"
          amount = var.budget_amount
          time_period = {
            start_date = var.budget_time_period_start
          }
          notifications = [
            {
              threshold      = var.budget_actual_threshold
              threshold_type = "Actual"      # Options: "Actual", "Forecasted"
              operator       = "GreaterThan" # Options: "GreaterThan", "LessThan", "EqualTo"
              contact_emails = var.cost_management_recipient_emails
            },
            {
              threshold      = var.budget_forecast_threshold
              threshold_type = "Forecasted"
              operator       = "GreaterThan"
              contact_emails = var.cost_management_recipient_emails
            }
          ]
        }
      }

      /* -------------------------------------------------------------------------- */
      /*                      Cost anomaly alert configuration                      */
      /* -------------------------------------------------------------------------- */
      azurerm_cost_anomaly_alert = {
        name            = "caa-connectivity-01-conn-${var.environment}-${var.location_short}"
        display_name    = "Connectivity Cost Anomaly Alert"
        email_subject   = "Connectivity Cost Anomaly Alert"
        email_addresses = var.cost_management_recipient_emails
      }

      /* -------------------------------------------------------------------------- */
      /*                     Cost management view configuration                     */
      /* -------------------------------------------------------------------------- */
      subscription_cost_management_view = {
        "cmv-connectivity-01-mtd-${var.environment}-${var.location_short}" = {
          name         = "cmv-connectivity-01-mtd-${var.environment}-${var.location_short}"
          display_name = "MTD - Connectivity Cost Management View"
          chart_type   = var.report_chart_type
          accumulated  = false
          report_type  = "Usage"       # Options: "Usage", "ActualCost"
          timeframe    = "MonthToDate" # Options: "MonthToDate", "WeekToDate", "YearToDate"
          dataset = {
            granularity = var.report_granularity
            aggregation = [
              {
                name        = "cost"
                column_name = "Cost"
              }
            ]
            grouping = [
              {
                name = "Product"
                type = "Dimension"
              }
            ]
          }
          pivot = [
            {
              name        = "ServiceName"
              function    = "Sum"
              column_name = "ServiceName"
              type        = "Dimension"
            },
            {
              name        = "ResourceLocation"
              function    = "Sum"
              column_name = "ResourceLocation"
              type        = "Dimension"
            },
            {
              name        = "ResourceGroupName"
              function    = "Sum"
              column_name = "ResourceGroupName"
              type        = "Dimension"
            }
          ]
        }
      }

      /* -------------------------------------------------------------------------- */
      /*                Scheduled action for cost management reports                */
      /* -------------------------------------------------------------------------- */
      cost_management_scheduled_action = {
        "cmsa-connectivity-01-weekly-${var.environment}-${var.location_short}" = {
          name                 = "cmsa-connectivity-01-weekly-${var.environment}-${var.location_short}"
          display_name         = "WEEKLY - Connectivity Scheduled Action"
          view_identifier      = "cmv-connectivity-01-mtd-${var.environment}-${var.location_short}"
          email_address_sender = var.cost_management_sender_email
          email_subject        = "Scheduled AZURE cost report - Connectivity"
          email_addresses      = var.cost_management_recipient_emails
          frequency            = "Weekly"
          start_date           = var.budget_time_period_start
          end_date             = var.budget_time_period_end
          hour_of_day          = var.report_weekly_hour
          days_of_week         = var.report_weekly_schedule
        }
      }
    },
    identity = {
      name                = "ide-consumption"
      resource_group_name = var.resource_group_names.identity
      subscription_consumption_budget = {
        "cbs-identity-01-ide-${var.environment}-${var.location_short}" = {
          name   = "cbs-identity-01-ide-${var.environment}-${var.location_short}"
          amount = var.budget_amount
          time_period = {
            start_date = var.budget_time_period_start
          }
          notifications = [
            {
              threshold      = var.budget_actual_threshold
              threshold_type = "Actual"
              operator       = "GreaterThan"
              contact_emails = var.cost_management_recipient_emails
            },
            {
              threshold      = var.budget_forecast_threshold
              threshold_type = "Forecasted"
              operator       = "GreaterThan"
              contact_emails = var.cost_management_recipient_emails
            }
          ]
        }
      }
      azurerm_cost_anomaly_alert = {
        name            = "caa-identity-01-ide-${var.environment}-${var.location_short}"
        display_name    = "Identity Cost Anomaly Alert"
        email_subject   = "Identity Cost Anomaly Alert"
        email_addresses = var.cost_management_recipient_emails
      }
      subscription_cost_management_view = {
        "cmv-identity-01-mtd-${var.environment}-${var.location_short}" = {
          name         = "cmv-identity-01-mtd-${var.environment}-${var.location_short}"
          display_name = "MTD - Identity Cost Management View"
          chart_type   = var.report_chart_type
          accumulated  = false
          report_type  = "Usage"
          timeframe    = "MonthToDate"
          dataset = {
            granularity = var.report_granularity
            aggregation = [
              {
                name        = "cost"
                column_name = "Cost"
              }
            ]
            grouping = [
              {
                name = "Product"
                type = "Dimension"
              }
            ]
          }
          pivot = [
            {
              name        = "ServiceName"
              function    = "Sum"
              column_name = "ServiceName"
              type        = "Dimension"
            },
            {
              name        = "ResourceLocation"
              function    = "Sum"
              column_name = "ResourceLocation"
              type        = "Dimension"
            },
            {
              name        = "ResourceGroupName"
              function    = "Sum"
              column_name = "ResourceGroupName"
              type        = "Dimension"
            }
          ]
        }
      }
      cost_management_scheduled_action = {
        "cmsa-identity-01-weekly-${var.environment}-${var.location_short}" = {
          name                 = "cmsa-identity-01-weekly-${var.environment}-${var.location_short}"
          display_name         = "WEEKLY - Identity Scheduled Action"
          view_identifier      = "cmv-identity-01-mtd-${var.environment}-${var.location_short}"
          email_address_sender = var.cost_management_sender_email
          email_subject        = "Scheduled AZURE cost report - Identity"
          email_addresses      = var.cost_management_recipient_emails
          frequency            = "Weekly"
          start_date           = var.budget_time_period_start
          end_date             = var.budget_time_period_end
          hour_of_day          = var.report_weekly_hour
          days_of_week         = var.report_weekly_schedule
        }
      }
    },
    empirics = {
      name                = "emp-consumption"
      resource_group_name = var.resource_group_names.empirics
      subscription_consumption_budget = {
        "cbs-empirics-01-emp-${var.environment}-${var.location_short}" = {
          name   = "cbs-empirics-01-emp-${var.environment}-${var.location_short}"
          amount = var.budget_amount
          time_period = {
            start_date = var.budget_time_period_start
          }
          notifications = [
            {
              threshold      = var.budget_actual_threshold
              threshold_type = "Actual"
              operator       = "GreaterThan"
              contact_emails = var.cost_management_recipient_emails
            },
            {
              threshold      = var.budget_forecast_threshold
              threshold_type = "Forecasted"
              operator       = "GreaterThan"
              contact_emails = var.cost_management_recipient_emails
            }
          ]
        }
      }
      azurerm_cost_anomaly_alert = {
        name            = "caa-empirics-01-emp-${var.environment}-${var.location_short}"
        display_name    = "Empirics Cost Anomaly Alert"
        email_subject   = "Empirics Cost Anomaly Alert"
        email_addresses = var.cost_management_recipient_emails
      }
      subscription_cost_management_view = {
        "cmv-empirics-01-mtd-${var.environment}-${var.location_short}" = {
          name         = "cmv-empirics-01-mtd-${var.environment}-${var.location_short}"
          display_name = "MTD - Empirics Cost Management View"
          chart_type   = var.report_chart_type
          accumulated  = false
          report_type  = "Usage"
          timeframe    = "MonthToDate"
          dataset = {
            granularity = var.report_granularity
            aggregation = [
              {
                name        = "cost"
                column_name = "Cost"
              }
            ]
            grouping = [
              {
                name = "Product"
                type = "Dimension"
              }
            ]
          }
          pivot = [
            {
              name        = "ServiceName"
              function    = "Sum"
              column_name = "ServiceName"
              type        = "Dimension"
            },
            {
              name        = "ResourceLocation"
              function    = "Sum"
              column_name = "ResourceLocation"
              type        = "Dimension"
            },
            {
              name        = "ResourceGroupName"
              function    = "Sum"
              column_name = "ResourceGroupName"
              type        = "Dimension"
            }
          ]
        }
      }
      cost_management_scheduled_action = {
        "cmsa-empirics-01-weekly-${var.environment}-${var.location_short}" = {
          name                 = "cmsa-empirics-01-weekly-${var.environment}-${var.location_short}"
          display_name         = "WEEKLY - Empirics Scheduled Action"
          view_identifier      = "cmv-empirics-01-mtd-${var.environment}-${var.location_short}"
          email_address_sender = var.cost_management_sender_email
          email_subject        = "Scheduled AZURE cost report - Empirics"
          email_addresses      = var.cost_management_recipient_emails
          frequency            = "Weekly"
          start_date           = var.budget_time_period_start
          end_date             = var.budget_time_period_end
          hour_of_day          = var.report_weekly_hour
          days_of_week         = var.report_weekly_schedule
        }
      }
    },
    management = {
      name                = "mgmt-consumption"
      resource_group_name = var.resource_group_names.management
      subscription_consumption_budget = {
        "cbs-management-01-mgmt-${var.environment}-${var.location_short}" = {
          name   = "cbs-management-01-mgmt-${var.environment}-${var.location_short}"
          amount = var.budget_amount
          time_period = {
            start_date = var.budget_time_period_start
          }
          notifications = [
            {
              threshold      = var.budget_actual_threshold
              threshold_type = "Actual"
              operator       = "GreaterThan"
              contact_emails = var.cost_management_recipient_emails
            },
            {
              threshold      = var.budget_forecast_threshold
              threshold_type = "Forecasted"
              operator       = "GreaterThan"
              contact_emails = var.cost_management_recipient_emails
            }
          ]
        }
      }
      azurerm_cost_anomaly_alert = {
        name            = "caa-management-01-mgmt-${var.environment}-${var.location_short}"
        display_name    = "Management Cost Anomaly Alert"
        email_subject   = "Management Cost Anomaly Alert"
        email_addresses = var.cost_management_recipient_emails
      }
      subscription_cost_management_view = {
        "cmv-management-01-mtd-${var.environment}-${var.location_short}" = {
          name         = "cmv-management-01-mtd-${var.environment}-${var.location_short}"
          display_name = "MTD - Management Cost Management View"
          chart_type   = var.report_chart_type
          accumulated  = false
          report_type  = "Usage"
          timeframe    = "MonthToDate"
          dataset = {
            granularity = var.report_granularity
            aggregation = [
              {
                name        = "cost"
                column_name = "Cost"
              }
            ]
            grouping = [
              {
                name = "Product"
                type = "Dimension"
              }
            ]
          }
          pivot = [
            {
              name        = "ServiceName"
              function    = "Sum"
              column_name = "ServiceName"
              type        = "Dimension"
            },
            {
              name        = "ResourceLocation"
              function    = "Sum"
              column_name = "ResourceLocation"
              type        = "Dimension"
            },
            {
              name        = "ResourceGroupName"
              function    = "Sum"
              column_name = "ResourceGroupName"
              type        = "Dimension"
            }
          ]
        }
      }
      cost_management_scheduled_action = {
        "cmsa-management-01-weekly-${var.environment}-${var.location_short}" = {
          name                 = "cmsa-management-01-weekly-${var.environment}-${var.location_short}"
          display_name         = "WEEKLY - Management Scheduled Action"
          view_identifier      = "cmv-management-01-mtd-${var.environment}-${var.location_short}"
          email_address_sender = var.cost_management_sender_email
          email_subject        = "Scheduled AZURE cost report - Management"
          email_addresses      = var.cost_management_recipient_emails
          frequency            = "Weekly"
          start_date           = var.budget_time_period_start
          end_date             = var.budget_time_period_end
          hour_of_day          = var.report_weekly_hour
          days_of_week         = var.report_weekly_schedule
        }
      }
    }
  }
}

/* -------------------------------------------------------------------------- */
/*                                 Module calls                               */
/* -------------------------------------------------------------------------- */
module "azurerm_consumption_connectivity" {
  source = "git::https://github.com/fabricesemti80/modules-tf-azure-infrastructure.git//tf-cost-management?ref=dev"
  providers = {
    azurerm = azurerm.connectivity
  }
  name                              = local.cost_consumption_configs.connectivity.name
  resource_group_name               = local.cost_consumption_configs.connectivity.resource_group_name
  environment_prefix                = var.environment
  subscription_consumption_budget   = local.cost_consumption_configs.connectivity.subscription_consumption_budget
  azurerm_cost_anomaly_alert        = local.cost_consumption_configs.connectivity.azurerm_cost_anomaly_alert
  subscription_cost_management_view = local.cost_consumption_configs.connectivity.subscription_cost_management_view
  cost_management_scheduled_action  = local.cost_consumption_configs.connectivity.cost_management_scheduled_action

  tags = merge(var.tags, {
    CreationTimeUTC = timestamp(),
    Environment     = "PROD"
  })
}

module "azurerm_consumption_identity" {
  source = "git::https://github.com/fabricesemti80/modules-tf-azure-infrastructure.git//tf-cost-management?ref=dev"
  providers = {
    azurerm = azurerm.identity
  }
  name                              = local.cost_consumption_configs.identity.name
  resource_group_name               = local.cost_consumption_configs.identity.resource_group_name
  environment_prefix                = var.environment
  subscription_consumption_budget   = local.cost_consumption_configs.identity.subscription_consumption_budget
  azurerm_cost_anomaly_alert        = local.cost_consumption_configs.identity.azurerm_cost_anomaly_alert
  subscription_cost_management_view = local.cost_consumption_configs.identity.subscription_cost_management_view
  cost_management_scheduled_action  = local.cost_consumption_configs.identity.cost_management_scheduled_action

  tags = merge(var.tags, {
    CreationTimeUTC = timestamp(),
    Environment     = "PROD"
  })
}

module "azurerm_consumption_empirics" {
  source = "git::https://github.com/fabricesemti80/modules-tf-azure-infrastructure.git//tf-cost-management?ref=dev"
  providers = {
    azurerm = azurerm.lz_empirics
  }
  name                              = local.cost_consumption_configs.empirics.name
  resource_group_name               = local.cost_consumption_configs.empirics.resource_group_name
  environment_prefix                = var.environment
  subscription_consumption_budget   = local.cost_consumption_configs.empirics.subscription_consumption_budget
  azurerm_cost_anomaly_alert        = local.cost_consumption_configs.empirics.azurerm_cost_anomaly_alert
  subscription_cost_management_view = local.cost_consumption_configs.empirics.subscription_cost_management_view
  cost_management_scheduled_action  = local.cost_consumption_configs.empirics.cost_management_scheduled_action

  tags = merge(var.tags, {
    CreationTimeUTC = timestamp(),
    Environment     = "PROD"
  })
}

module "azurerm_consumption_management" {
  source = "git::https://github.com/fabricesemti80/modules-tf-azure-infrastructure.git//tf-cost-management?ref=dev"
  providers = {
    azurerm = azurerm.management
  }
  name                              = local.cost_consumption_configs.management.name
  resource_group_name               = local.cost_consumption_configs.management.resource_group_name
  environment_prefix                = var.environment
  subscription_consumption_budget   = local.cost_consumption_configs.management.subscription_consumption_budget
  azurerm_cost_anomaly_alert        = local.cost_consumption_configs.management.azurerm_cost_anomaly_alert
  subscription_cost_management_view = local.cost_consumption_configs.management.subscription_cost_management_view
  cost_management_scheduled_action  = local.cost_consumption_configs.management.cost_management_scheduled_action

  tags = merge(var.tags, {
    CreationTimeUTC = timestamp(),
    Environment     = "PROD"
  })
}

source

Without getting lost in the details too much, what we get from this is cost management view for all the subscriptions, similar to this:

alt text

(link to these are periodically sent to the relevant teams)…

…and periodic exports…

alt text

…stored in a blob storage

alt text

We take care of building the storage account (in one of the subscriptions) and the container inside of this, that will contain the exports from all the subscriptions in the scope. We also create periodic - in my case, weekly - reports to show the month-to-date usage and help management keeping an eye on Azure cost vs budget.

The exports of course are more usaefull for the teams interested in details and invoices.

Code #

To be continued #

to be continued