Cost insights setup¶
Deze pagina is voor het platformteam. Ze beschrijft hoe je de kostenweergaven (Insights) voor een omgeving inschakelt. Het is een referentieontwerp: resourcenamen, scopes en instellingen kunnen in jouw deployment afwijken.
Dataflow¶
flowchart LR
be[Portal backend<br/>scheduled tasks] -->|creates exports| cm[Cost Management]
cm -->|FOCUS Parquet, Snappy| st[(Storage account<br/>billingdata container)]
cm -->|FOCUS Parquet, Snappy| dl[(Data Lake Gen2 accounts<br/>optional)]
st --> adf[Data Factory<br/>daily pipeline, 04:00]
adf --> sql[(Azure SQL<br/>tables and views)]
sql --> be2[Portal backend<br/>queries at runtime]
dl -.-> pg[(PostgreSQL<br/>azure_storage extension)]
De portal maakt en onderhoudt de exports zelf. Een Data Factory-pipeline draait dagelijks (trigger om 04:00 W. Europe-tijd), laadt de Parquet-bestanden in Azure SQL en ververst de views die de portal bevraagt.
Stappen¶
| Stap | Wat |
|---|---|
| 1 | Voeg de module cost_insights toe aan de omgevingsroot en koppel de outputs aan de module backstage |
| 2 | Voeg private connections toe voor de SQL server, als de provider vanuit zijn eigen netwerk deployt |
| 3 | Stel de instellingen voor kostenexports in, zodat de portal de exports configureert |
| 4 | Deploy in twee rondes en controleer |
1. De module¶
Voeg voor elke omgevingsroot de module cost_insights toe. De inputs komen uit outputs van bestaande modules (spoke canvas, Key Vault, naming en de module backstage).
module "cost_insights" {
source = "../../modules/cost_insights"
location = module.canvas.location
environment = module.canvas.environment
workload_short_name = module.canvas.workload_short_name
subnets = module.canvas.subnets
log_analytics_workspace_id = module.canvas.log_analytics_workspace_id
key_vault_id = module.key_vault.id
data_factory_name = module.naming.data_factory.name
# One Data Lake Gen2 account per key. The key is part of the resource name,
# so keep it stable: changing it recreates the account.
datalake_storage_accounts = {
default = {}
}
backstage_user_assigned_identity = {
resource_name = module.backstage.user_assigned_identity.resource_name
client_id = module.backstage.user_assigned_identity.client_id
}
}
De module maakt een eigen resource group aan met:
| Resource | Doel |
|---|---|
| Azure Data Factory | Laadt de exportbestanden in SQL. Heeft een alert bij fouten. |
| Storage account | Ontvangt exports in de container billingdata en bewaart de auditlogs van Data Factory |
| Data Lake Gen2 storage account | Eén per sleutel in datalake_storage_accounts, met eigen exports |
| Azure SQL server en database | Verwerkte billingdata. De standaard-SKU is S0 en de maximale grootte 150 GB. |
| User-assigned managed identities | Toegang met minimale rechten tussen Data Factory, storage en SQL |
| Key Vault keys | Customer-managed encryptie voor storage en SQL |
Een entry in datalake_storage_accounts kan readers opsommen: een map met Entra object-ID's als sleutel. Elke reader krijgt Storage Blob Data Reader op dat account, zodat een ander team, zoals een dataplatform, de geëxporteerde bestanden kan lezen. De map is standaard leeg, dus niemand buiten de portal kan de exports lezen totdat je een reader toevoegt.
De module gebruikt de Terraform provider mssql om databasegebruikers te beheren, dus de omgevingsroot heeft dit providerblok nodig. De runner die Terraform toepast, heeft daarom een netwerkpad naar de SQL server nodig.
provider "mssql" {
azure_auth = {}
hostname = module.cost_insights.database_server
port = module.cost_insights.database_port
}
Geef de outputs door aan de module backstage: de resource-ID's van de storage accounts, de SQL server, de poort en de databasenaam (als app settings die met APP_CONFIG_costInsights_database_ beginnen) en het voorvoegsel van de exportnaam.
module "backstage" {
# ... existing configuration ...
cost_exports_storage_account_resource_id = module.cost_insights.storage_account_resource_id
cost_exports_datalake_storage_accounts = {
default = {
resource_id = module.cost_insights.storage_account_datalake_resource_ids["default"]
role_assignment_enabled = true
}
}
additional_app_settings = {
APP_CONFIG_costInsights_database_server = module.cost_insights.database_server
APP_CONFIG_costInsights_database_port = module.cost_insights.database_port
APP_CONFIG_costInsights_database_database = module.cost_insights.database_name
APP_CONFIG_cost_exports_exportNamePrefix = "<prefix>_"
}
}
Gebruik de module-inputs, niet de losse app settings
Het instellen van de storage-inputs van de module backstage maakt de role assignments op elk storage account aan. De gelijkwaardige app settings alleen doen dat niet.
De portal laadt de kostenbackend alleen als costInsights.database.server is ingesteld. Zonder die instelling zijn de kostenpagina's niet beschikbaar.
2. Private connections¶
Als de provider vanuit zijn eigen netwerk deployt, voeg dan een private connection voor de SQL server (subresource sqlServer) toe in de platformroot, keur die goed aan de kant van de klant en registreer de DNS-entry. Zie Een klant onboarden.
Kostenexports die de portal configureert¶
Met de bovenstaande instellingen draait de kostenbackend geplande taken die per subscription binnen de scope één FOCUS-schema Parquet-export (Snappy) aanmaken. Elke export dekt de data van de maand tot nu toe.
| Taak | Doel |
|---|---|
configure-azure-cost-exports |
cost.exports: het storage account dat Data Factory leest |
configure-azure-cost-exports-datalake-<prefix> |
Eén taak per entry in cost.dataLakeExports: een eigen Data Lake-account |
| Gedrag | Detail |
|---|---|
| Batch | Elke run behandelt een kleine batch subscriptions (standaard 25) |
| Venster | Draait alleen in een venster buiten kantooruren en gaat over meerdere runs door tot alle subscriptions zijn geconfigureerd |
| Bestaande exports | Worden herkend en blijven staan |
| Herverwerkingsinterval | Standaard één dag |
| Dry run | Een instelling dryRun logt wat zou veranderen zonder exports aan te maken |
| Prefix | Het voorvoegsel van de exportnaam is standaard de naam van het storage account plus _ |
| State | Elk doel houdt zijn eigen state bij, zodat taken elkaar niet storen |
Beperk de scope van de subscriptions als "elke subscription die de identity kan zien" te breed is:
cost:
exports:
includeManagementGroups: ['<management-group>']
dataLakeExports:
default:
includeManagementGroups: ['<management-group>']
Vereisten aan de Azure-kant:
- De managed identity van de portal heeft Cost Management Contributor nodig op de management group binnen de scope en leestoegang tot beide storage accounts (Prerequisites).
Microsoft.CostManagementmoet zijn geregistreerd op elke subscription binnen de scope. Zo niet, dan logt de portal een waarschuwing en probeert het bij de volgende run opnieuw.
Directe ingestie in PostgreSQL¶
De portal kan de Parquet-bestanden uit de Data Lake-accounts importeren in zijn eigen PostgreSQL-database. De module postgresql bereidt de server voor: die heeft een system-assigned managed identity, de module backstage geeft die Storage Blob Data Reader op elk Data Lake-account en de extensie azure_storage is toegestaan en vooraf geladen. Pas de wijziging die de identity aanmaakt toe vóór de wijziging die de rol toewijst, omdat Terraform geen role assignment voor een onbekende principal kan plannen. Het wijzigen van shared_preload_libraries herstart de server.
De bron beschrijft dit pad als voorbereiding op een toekomstige fase waarin de portal kostendata zelf inleest en combineert met catalogdata.
Migreren vanaf een eerdere exportpipeline¶
Als een klant nog een eerdere, scriptgebaseerde exportpipeline draait:
- Deploy deze versie met hetzelfde voorvoegsel van de exportnaam dat de pipeline per omgeving gebruikt, zodat beide dezelfde export als doel hebben.
- Controleer de backendlogs op de taak
configure-azure-cost-exportsen bevestig in de Azure portal dat de exports bestaan. - Schakel de oude pipeline uit en verwijder hem.
Deployen en controleren¶
- Deploy de eerste omgeving. De eerste run slaat het aanmaken van databasetabellen en privileges over, omdat de resource group van Data Factory nog niet bestaat, dus doe een tweede deployment.
- Keur de private endpoint connections goed en werk DNS bij.
- Deploy de volgende omgeving op dezelfde manier.
- Bevestig dat de portal de exports heeft geconfigureerd en dat er minstens één bestand in de container
billingdatais aangekomen. Exports draaien meestal binnen 24 uur en de ingestie 's nachts, dus het kan één tot twee dagen duren voordat er data verschijnt.