From f15f739d348722c6006e0ad87639c95942f0722e Mon Sep 17 00:00:00 2001 From: Daniel Truong Date: Sun, 6 Sep 2026 19:46:56 +0000 Subject: [PATCH] fix(azure): keep the summary rule columns on EagleEventsDaily_CL The tables PUT replaces the column list, so every deploy asked ARM to drop the four _Bin*/_Rule* columns the summary rule adds. Declare them on the table (not the DCR stream) so what-if is clean and the rule keeps its fields. isActive on the rule is read-only; the what-if line for it is noise. --- azure/modules/event-logs.bicep | 35 +++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/azure/modules/event-logs.bicep b/azure/modules/event-logs.bicep index 11601e0..b9ded60 100644 --- a/azure/modules/event-logs.bicep +++ b/azure/modules/event-logs.bicep @@ -171,9 +171,12 @@ resource auditTable 'Microsoft.OperationalInsights/workspaces/tables@2025-07-01' } // Written twice, same as the two above: once as the table schema, once as the DCR stream the history -// import posts to. -var dailyColumns = [ - { name: 'TimeGenerated', type: 'datetime' } +// import posts to. Two orderings of one list rather than two lists, because what-if compares a +// column array by position: a re-ordered copy of the same columns reads as a dozen renames. Each +// list keeps the order its own API returns — `TimeGenerated` first in the stream, last in the table. +var timeGeneratedColumn = { name: 'TimeGenerated', type: 'datetime' } + +var dailyRollupColumns = [ { name: 'Day', type: 'datetime' } { name: 'SourceApp', type: 'string' } { name: 'EventName', type: 'string' } @@ -189,6 +192,24 @@ var dailyColumns = [ { name: 'Users', type: 'long' } ] +var dailyStreamColumns = concat([ timeGeneratedColumn ], dailyRollupColumns) + +// Four columns the summary rule adds to its own destination table, declared here because the tables +// API replaces the column list on every PUT — "to delete a custom column, send the same PUT request +// but omit the column from the columns array" — so leaving them out asks every deployment to delete +// them out from under the rule. +// +// Table only, never the DCR stream: nothing outside the rule writes them, and a stream declaration is +// a contract with the history import, which posts the columns above and no others. +var summaryRuleColumns = [ + { name: '_BinSize', type: 'long' } + { name: '_BinStartTime', type: 'datetime' } + { name: '_RuleLastModifiedTime', type: 'datetime' } + { name: '_RuleName', type: 'string' } +] + +var dailyTableColumns = concat(summaryRuleColumns, dailyRollupColumns, [ timeGeneratedColumn ]) + // The rollup destination is declared rather than left to the summary rule. // // The rule creates the table on its first run if it is absent, but it creates it with the WORKSPACE @@ -208,7 +229,7 @@ resource dailyTable 'Microsoft.OperationalInsights/workspaces/tables@2025-07-01' totalRetentionInDays: dailyRetentionDays schema: { name: dailyTableName - columns: dailyColumns + columns: dailyTableColumns } } } @@ -234,7 +255,7 @@ resource dcr 'Microsoft.Insights/dataCollectionRules@2023-03-11' = { // scripts/import-penguin-history.js, which posts historical rollup rows through the ingestion // API — the only way into the table from outside the workspace. 'Custom-${dailyTableName}': { - columns: dailyColumns + columns: dailyStreamColumns } } destinations: { @@ -319,6 +340,10 @@ resource readerAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' // No time filter and no bin on the aggregation window: `binSize` already defines it, and the // destination rows carry `_BinStartTime`. `Day` is projected anyway because the history import needs // the column, and at binSize 1440 the day bin and the rule's own bin coincide. +// +// `isActive` is absent because it cannot be set: it is read-only on this type (bicep BCP073), so the +// what-if line proposing its removal is one ARM does not act on. The live rule stayed active across +// a deployment of this template, and kept writing rollups afterwards. resource eventsDailyRollup 'Microsoft.OperationalInsights/workspaces/summaryLogs@2025-07-01' = { parent: workspace name: 'eagle-events-daily'