Ich sag dir, wie’s ist.
Dein Azure-Setup wächst. Jeden Tag kommen Ressourcen dazu.
Irgendwer klickt was im Portal.
Die Rechnung wächst mit, aber keiner weiß so richtig, warum.
Und plötzlich bist du der, der’s richten soll.
Warum ich darüber schreibe
Ich bin Matthias. Seit über fünf Jahren helfe ich Teams dabei, ihre Azure-Umgebungen nicht nur stabil, sondern auch wirtschaftlich sinnvoll zu betreiben.
Ich komme oft ins Spiel, wenn das Kind schon halb im Brunnen liegt: Ressourcen-Chaos, fehlende Tags, Auto-Scaling gibt’s nur auf dem Papier und das Controlling fragt zum dritten Mal nach einer Auswertung, die niemand liefern kann.
So konnte ich lernen:
FinOps funktioniert nur dann, wenn es Teil des Codes ist.
Und Terraform ist genau das Werkzeug dafür.
Alte Denkweise, neue Realität
Viele denken immer noch in Silos: Da gibt’s den Betrieb, da gibt’s die Finanzen, und irgendwo läuft dann halt Azure.
Und viel zu oft wird Infrastruktur gebaut wie früher, nur eben in der Cloud.
Das klappt nicht mehr.
Was ich lernen musste: Kosten lassen sich nicht „hinten raus“ optimieren.
Sie müssen vorne mitgedacht werden.
Beim Design.
Beim Deployment.
Und genau da kann man Terraform richtig gut einsetzen.
Mach es auf die moderne Art!
Wenn du FinOps wirklich leben willst, dann brauchst du mehr als ein paar Dashboards.
Du brauchst Code und Policies, die nicht verhandelbar sind.
Drei Terraform-Praktiken, die mir geholfen haben
1. Auto-Scaling-Regeln als Code
Früher hab ich Ressourcen „für den Worst Case“ ausgelegt.
Heute schreibe ich lieber Terraform-Module, die Autoscaling in Azure sauber und mit echten Nutzungsdaten konfigurieren.
Beispiel: App Services, die nachts „schlafen“, weil’s keiner merkt, aber das Konto freut’s.
Was meine ich damit: je nach AppService Plan können die Ressourcen am Wochenende oder eben Nachts auf ein Minimum reduziert werden.
Zu bestimmten Zeiten werden diese wieder erweitert und können automatisch für Lastspitzen angepasst werden.
Das ganze funktioniert mit der Terraform azurerm_monitor_autoscale_setting
Ressource.
Hier ein komplettes Beispiel, mit allem was nötig ist.
# ─── 0) Gemeinsame FinOps-Tags ─────────────────────────────────────────────
locals {
finops_tags = {
CostCenter = "CC-4711"
Project = "WebShopX"
Environment = "prod"
Owner = "matthias.braun@contoso.com"
Service = "AzureAppService"
ManagedBy = "Terraform"
}
}
# ─── 1) Resource Group (RG) ────────────────────────────────────────────────
resource "azurerm_resource_group" "rg" {
name = "rg-autoscaling-test-weu"
location = "West Europe"
tags = local.finops_tags
}
# ─── 2) App Service Plan (ASP) ─────────────────────────────────────────────
resource "azurerm_app_service_plan" "asp" {
name = "asp-webshopx-weu-s1"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
sku {
tier = "Standard"
size = "S1"
}
tags = merge(local.finops_tags, { Component = "AppServicePlan" })
}
# ─── 3) Web App (APP) ──────────────────────────────────────────────────────
resource "random_id" "rand" {
byte_length = 4
}
resource "azurerm_app_service" "app" {
name = "app-webshopx-${random_id.rand.hex}"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
app_service_plan_id = azurerm_app_service_plan.asp.id
site_config {
linux_fx_version = "DOCKER|mcr.microsoft.com/dotnet/aspnet:8.0"
http2_enabled = true
minimum_tls_version = "1.2"
}
identity { type = "SystemAssigned" }
tags = merge(local.finops_tags, { Component = "WebApp" })
}
# ─── 4) Autoscale-Setting (AUTOSCALE) ──────────────────────────────────────
resource "azurerm_monitor_autoscale_setting" "autoscale" {
name = "autoscale-asp-weekend"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
target_resource_id = azurerm_app_service_plan.asp.id
# --- Weekday: normaler Betrieb -----------------------------------------
profile {
name = "WeekdayDefault"
capacity { default = 3 minimum = 2 maximum = 10 }
rule {
metric_trigger {
metric_name = "CpuPercentage"
metric_resource_id = azurerm_app_service_plan.asp.id
time_grain = "PT1M"
statistic = "Average"
time_window = "PT5M"
time_aggregation = "Average"
operator = "GreaterThan"
threshold = 75
}
scale_action {
direction = "Increase"
type = "ChangeCount"
value = "1"
cooldown = "PT2M"
}
}
rule {
metric_trigger {
metric_name = "CpuPercentage"
metric_resource_id = azurerm_app_service_plan.asp.id
time_grain = "PT1M"
statistic = "Average"
time_window = "PT5M"
time_aggregation = "Average"
operator = "LessThan"
threshold = 25
}
scale_action {
direction = "Decrease"
type = "ChangeCount"
value = "1"
cooldown = "PT2M"
}
}
}
# --- Weekend: Sparflamme -----------------------------------------------
profile {
name = "WeekendReduced"
capacity { default = 1 minimum = 1 maximum = 3 }
recurrence {
timezone = "W. Europe Standard Time"
days = ["Saturday", "Sunday"]
hours = [0]
minutes = [0]
}
}
notification {
email {
send_to_subscription_administrator = true
send_to_subscription_co_administrator = true
custom_emails = ["cloudops@contoso.com"]
}
}
tags = merge(local.finops_tags, { Component = "AutoscaleSetting" })
}
2. Nutzungsrichtlinien automatisieren
In fast jedem Projekt: Irgendwer erstellt VMs in Regionen, die keiner braucht.
Oder in Größen, die keiner zahlen will.
Mit Azure Policy und Terraform kannst du Regeln aufstellen, die automatisch greifen:
Nur bestimmte VM-Typen.
Nur bestimmte Regionen.
Und bitte immer mit Tags.
Das nimmt Diskussionen raus, weil der Code oder die Policy, die Regeln durchsetzt.
Nicht du.
Terraform integriert das als Ressource azurerm_policy_definition.
Beispiel einer Policy, für die Einschränkung der Azure Region auf West Europe (Niederlande, Amsterdam).
# ─── 0) Gemeinsame FinOps-Tags ────────────────────────────────────────────
locals {
finops_tags = {
CostCenter = "CC-4711"
Project = "WebShopX"
Environment = "prod"
Owner = "matthias.braun@contoso.com"
Service = "Governance"
ManagedBy = "Terraform"
}
}
# ─── 1) Policy Definition (POL) – unverändert ─────────────────────────────
resource "azurerm_policy_definition" "pol" {
name = "pol-allow-location-weu"
policy_type = "Custom"
mode = "All"
display_name = "Allowed Location: West Europe only"
description = "Erlaubt Deployments ausschließlich im Azure-Bereich 'West Europe'."
policy_rule = jsonencode({
if = {
field = "location"
notIn = ["westeurope"]
}
then = {
effect = "Deny"
}
})
metadata = jsonencode({ category = "General", version = "1.0.0" })
parameters = jsonencode({})
tags = merge(local.finops_tags, { Component = "PolicyDefinition" })
}
# ─── 2) Datenquelle: Management-Group Landingzones ────────────────────────
data "azurerm_management_group" "landingzones" {
# 'name' ist der Management-Group-ID (nicht Display-Name) – prüf in Azure-Portal.
name = "landingzones"
}
# ─── 3) Policy Assignment (POL_ASSIGN) auf Management-Group ───────────────
resource "azurerm_policy_assignment" "pol_assign" {
name = "pa-allow-location-weu"
display_name = "Enforce West Europe"
policy_definition_id = azurerm_policy_definition.pol.id
# Scope auf Management-Group
scope = data.azurerm_management_group.landingzones.id
description = "Blockiert Deployments in anderen Azure-Regionen als West Europe."
location = "West Europe" # Pflichtfeld
tags = merge(local.finops_tags, { Component = "PolicyAssignment" })
}
3. Tagging – konsequent und automatisiert
Ich habe zu oft gesehen, wie am Ende keiner wusste, wem was gehört.
Dabei ist Tagging kein vielleicht, sondern Pflicht.
Deine Superpower.
Mit Terraform kannst du Tags fest in dein Workflow und Modul-Design einbauen.
So entsteht Klarheit und kein Rätselraten.
tags = {
"cost-center" = "CC-4711"
„project“ = "WebShopX„
"env" = "prod"
}
Das ist nicht fancy, aber es rettet dir am Monatsende den Nacken.
FinOps ist kein Tool, sondern eine Haltung
Ich hab’s lernen müssen: Man kann nicht alles manuell kontrollieren, aber man kann es automatisieren.
Terraform ist für mich nicht nur ein technisches Tool.
Es ist ein Filter.
Alles, was da durchgeht, muss nachvollziehbar, verantwortbar und effizient sein.
Und warum mir das wichtig ist
Ich bin Familienmensch.
Ich steh morgens früh auf, trink meinen Kaffee während ich die Pausenbrote schmiere, danach machen Sabine und ich die Kids startklar.
Jeder hat seine Aufgabe. Und jeder weiß, was zu tun ist.
Was ich daran mag: es funktioniert.
Und ich mag Dinge, die funktionieren.
Und Menschen, die Verantwortung übernehmen.
Azure ist mächtig.
Aber nur, wenn du’s im Griff hast.
Und dabei will ich helfen.
Lass uns reden.
Wenn du wissen willst, wie das bei dir aussehen könnte, schreib mir.
Oder geh erstmal ne Runde laufen.
Manchmal kommt die Lösung, wenn man nicht davor sitzt.
Danke für deine Zeit!
Matthias
Soll ich die Terraform-Beispiele auf GitHub bereitstellen?
Wäre das hilfreich für dich oder dein Team?
Lass mir gern kurz deine Meinung da.
Bin gespannt ☺️