From d30e7e413712a7a50ead2ca11a4dcb02a5588840 Mon Sep 17 00:00:00 2001 From: Michael Flanakin Date: Wed, 9 Sep 2026 13:16:48 -0700 Subject: [PATCH 1/3] feat(hubs): add opt-in parameter to disable shared key access on hub storage accounts Adds a disableStorageSharedKeyAccess parameter (default false, preserving current behavior) threaded through main.bicep -> hub.bicep -> HubProperties options -> the publisher storage account (hub-app.bicep) and the private-routing script storage account (infrastructure.bicep). When enabled, allowSharedKeyAccess is set to false on both storage accounts so only Microsoft Entra ID and managed identity authentication are allowed. Nothing internally depends on the storage account key: Data Factory already authenticates via managed identity + Storage Blob Data Contributor RBAC, and the deployment-script storage account uses MSI too. Shared Key access is a freely mutable property (not irreversible like Key Vault purge protection), so no propagation-delay caveat is needed. Also exposes the parameter in createUiDefinition.json (new "Storage account security" section under Advanced) and in Deploy-FinOpsHub.ps1 as a -DisableStorageSharedKeyAccess switch, version-gated for template v16+, following the review feedback from PR #2249 that flagged these two surfaces as easy to miss when adding a new hub template parameter. Closes #2312 Co-Authored-By: Claude Sonnet 5 --- docs-mslearn/toolkit/changelog.md | 1 + src/powershell/Public/Deploy-FinOpsHub.ps1 | 12 +++++++ .../Tests/Unit/Deploy-FinOpsHub.Tests.ps1 | 33 +++++++++++++++++++ .../finops-hub/createUiDefinition.json | 23 +++++++++++++ src/templates/finops-hub/main.bicep | 4 +++ .../Core/infrastructure.bicep | 2 +- .../finops-hub/modules/fx/hub-app.bicep | 2 +- .../finops-hub/modules/fx/hub-types.bicep | 6 ++++ src/templates/finops-hub/modules/hub.bicep | 4 +++ 9 files changed, 85 insertions(+), 2 deletions(-) diff --git a/docs-mslearn/toolkit/changelog.md b/docs-mslearn/toolkit/changelog.md index 767edf7ba..3f4ec0fd4 100644 --- a/docs-mslearn/toolkit/changelog.md +++ b/docs-mslearn/toolkit/changelog.md @@ -29,6 +29,7 @@ The following section lists features and enhancements that are currently in deve - **Added** - Added VNet and private network modes, including opt-in NAT Gateway support for private mode; NAT Gateway incurs additional cost when enabled ([#2163](https://github.com/microsoft/finops-toolkit/pull/2163)). + - Added an opt-in `disableStorageSharedKeyAccess` parameter (and matching `-DisableStorageSharedKeyAccess` switch in [Deploy-FinOpsHub](powershell/hubs/deploy-finopshub.md)) to disable Shared Key (storage account key) authentication on hub storage accounts. Data Factory already authenticates using managed identity and RBAC, so enabling this does not affect data ingestion. Default: false, to avoid changing the authentication model on existing deployments ([#2312](https://github.com/microsoft/finops-toolkit/issues/2312)). - **Changed** - Clarified that the FinOps toolkit exclusively manages the FinOps hub virtual network and documented customer-managed private endpoints as the preferred private-access topology, with virtual network peering as a secondary option ([#2156](https://github.com/microsoft/finops-toolkit/issues/2156)). - Replaced redundant `tolower()` comparisons in hub KQL with case-insensitive operators (`has`, `=~`, `!~`) so the engine can use the term index instead of scanning every row ([#2213](https://github.com/microsoft/finops-toolkit/issues/2213)). diff --git a/src/powershell/Public/Deploy-FinOpsHub.ps1 b/src/powershell/Public/Deploy-FinOpsHub.ps1 index 6140e0af8..5ddb5f310 100644 --- a/src/powershell/Public/Deploy-FinOpsHub.ps1 +++ b/src/powershell/Public/Deploy-FinOpsHub.ps1 @@ -34,6 +34,9 @@ .PARAMETER EnablePurgeProtection Optional. Enable purge protection for the Key Vault. Default = false. + .PARAMETER DisableStorageSharedKeyAccess + Optional. Disable Shared Key (storage account key) authentication on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. Data Factory already authenticates using managed identity and RBAC, so this does not affect data ingestion. Default = false. + .PARAMETER RemoteHubStorageUri Optional. Storage account to push data to for ingestion into a remote hub. @@ -145,6 +148,10 @@ function Deploy-FinOpsHub [switch] $EnablePurgeProtection, + [Parameter()] + [switch] + $DisableStorageSharedKeyAccess, + [Parameter()] [string] $RemoteHubStorageUri, @@ -322,6 +329,11 @@ function Deploy-FinOpsHub $parameterSplat.TemplateParameterObject.Add('enablePurgeProtection', $EnablePurgeProtection.IsPresent) } + if ($Version -eq 'latest' -or [version]$Version -ge '16.0') + { + $parameterSplat.TemplateParameterObject.Add('disableStorageSharedKeyAccess', $DisableStorageSharedKeyAccess.IsPresent) + } + # Only pass enableNatGateway when private mode is requested. This keeps public/vnet # deployments compatible with template versions that predate the parameter. if ($effectiveNetworkMode -eq 'private' -and ($Version -eq 'latest' -or [version]$Version -ge '15.0')) diff --git a/src/powershell/Tests/Unit/Deploy-FinOpsHub.Tests.ps1 b/src/powershell/Tests/Unit/Deploy-FinOpsHub.Tests.ps1 index 1bb4512a0..6b0eb176e 100644 --- a/src/powershell/Tests/Unit/Deploy-FinOpsHub.Tests.ps1 +++ b/src/powershell/Tests/Unit/Deploy-FinOpsHub.Tests.ps1 @@ -268,5 +268,38 @@ InModuleScope 'FinOpsToolkit' { } -Times 1 } } + + Context 'Storage shared key access' { + BeforeAll { + Mock -CommandName 'Get-AzResourceGroup' -MockWith { return @{ ResourceGroupName = $rgName } } + Mock -CommandName 'New-AzResourceGroup' + Mock -CommandName 'Save-FinOpsHubTemplate' + Mock -CommandName 'Initialize-FinOpsHubDeployment' + $templateFile = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath 'FinOps/finops-hub-v1.0.0/main.bicep' + Mock -CommandName 'Get-ChildItem' -MockWith { return @{ FullName = $templateFile } } + Mock -CommandName 'New-AzResourceGroupDeployment' + } + + It 'Should default to allowing Shared Key access' { + { Deploy-FinOpsHub -Name $hubName -ResourceGroup $rgName -Location $location -Version 'latest' } | Should -Not -Throw + Should -Invoke -CommandName 'New-AzResourceGroupDeployment' -ParameterFilter { + $TemplateParameterObject.disableStorageSharedKeyAccess -eq $false + } -Times 1 + } + + It 'Should pass disableStorageSharedKeyAccess when -DisableStorageSharedKeyAccess is specified' { + { Deploy-FinOpsHub -Name $hubName -ResourceGroup $rgName -Location $location -DisableStorageSharedKeyAccess -Version 'latest' } | Should -Not -Throw + Should -Invoke -CommandName 'New-AzResourceGroupDeployment' -ParameterFilter { + $TemplateParameterObject.disableStorageSharedKeyAccess -eq $true + } -Times 1 + } + + It 'Should not pass disableStorageSharedKeyAccess when targeting a version older than 16.0' { + { Deploy-FinOpsHub -Name $hubName -ResourceGroup $rgName -Location $location -DisableStorageSharedKeyAccess -Version '15.0' } | Should -Not -Throw + Should -Invoke -CommandName 'New-AzResourceGroupDeployment' -ParameterFilter { + -not $TemplateParameterObject.ContainsKey('disableStorageSharedKeyAccess') + } -Times 1 + } + } } } \ No newline at end of file diff --git a/src/templates/finops-hub/createUiDefinition.json b/src/templates/finops-hub/createUiDefinition.json index 27c8de8dc..4074d880f 100644 --- a/src/templates/finops-hub/createUiDefinition.json +++ b/src/templates/finops-hub/createUiDefinition.json @@ -878,6 +878,28 @@ ], "visible": true }, + { + "name": "storageSecurity", + "type": "Microsoft.Common.Section", + "label": "Storage account security", + "elements": [ + { + "name": "disableStorageSharedKeyAccessIntro", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Data Factory always connects to hub storage accounts using its managed identity, so disabling Shared Key access does not affect data ingestion. This can be changed after deployment." + } + }, + { + "name": "disableStorageSharedKeyAccess", + "type": "Microsoft.Common.CheckBox", + "label": "Disable Shared Key access", + "toolTip": "Disables Shared Key (storage account key) authentication on hub storage accounts, requiring Microsoft Entra ID or managed identity authentication instead. Recommended for compliance requirements that prohibit key-based storage access." + } + ], + "visible": true + }, { "name": "managedExports", "type": "Microsoft.Common.Section", @@ -998,6 +1020,7 @@ "fabricQueryUri": "[if(equals(basics('analyticsBackend').analyticsEngine, 'fabric'), basics('analyticsBackend').fabricQueryUri, '')]", "fabricCapacityUnits": "[if(equals(basics('analyticsBackend').analyticsEngine, 'fabric'), basics('analyticsBackend').fabricCapacity, '')]", "enableInfrastructureEncryption": "[steps('advanced').storage.enableInfrastructureEncryption]", + "disableStorageSharedKeyAccess": "[steps('advanced').storageSecurity.disableStorageSharedKeyAccess]", "enableManagedExports": "[steps('advanced').managedExports.enableManagedExports]", "enableRecommendations": "[steps('recommendations').enableRecommendations]", "enableAHBRecommendations": "[steps('recommendations').optional.enableAHBRecommendations]", diff --git a/src/templates/finops-hub/main.bicep b/src/templates/finops-hub/main.bicep index d5b3a8850..85464b601 100644 --- a/src/templates/finops-hub/main.bicep +++ b/src/templates/finops-hub/main.bicep @@ -26,6 +26,9 @@ param storageSku string = 'Premium_LRS' @description('Optional. Enable infrastructure encryption on the storage account. Default = false.') param enableInfrastructureEncryption bool = false +@description('Optional. Disable Shared Key (storage account key) authentication on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. Data Factory already authenticates using managed identity and RBAC, so this does not affect data ingestion. Default = false.') +param disableStorageSharedKeyAccess bool = false + @description('Optional. Enable purge protection for the Key Vault. Default: false.') param enablePurgeProtection bool = false @@ -178,6 +181,7 @@ module hub 'modules/hub.bicep' = { // eventGridLocation: eventGridLocation storageSku: storageSku enableInfrastructureEncryption: enableInfrastructureEncryption + disableStorageSharedKeyAccess: disableStorageSharedKeyAccess enablePurgeProtection: enablePurgeProtection enableManagedExports: enableManagedExports enableRecommendations: enableRecommendations diff --git a/src/templates/finops-hub/modules/Microsoft.FinOpsHubs/Core/infrastructure.bicep b/src/templates/finops-hub/modules/Microsoft.FinOpsHubs/Core/infrastructure.bicep index 4c9934b94..f62351ff4 100644 --- a/src/templates/finops-hub/modules/Microsoft.FinOpsHubs/Core/infrastructure.bicep +++ b/src/templates/finops-hub/modules/Microsoft.FinOpsHubs/Core/infrastructure.bicep @@ -378,7 +378,7 @@ resource scriptStorageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = i tags: getHubTags(hub, 'Microsoft.Storage/storageAccounts') properties: { supportsHttpsTrafficOnly: true - allowSharedKeyAccess: true + allowSharedKeyAccess: !hub.options.disableStorageSharedKeyAccess isHnsEnabled: false minimumTlsVersion: 'TLS1_2' allowBlobPublicAccess: false diff --git a/src/templates/finops-hub/modules/fx/hub-app.bicep b/src/templates/finops-hub/modules/fx/hub-app.bicep index b176c05a5..d7e3638c4 100644 --- a/src/templates/finops-hub/modules/fx/hub-app.bicep +++ b/src/templates/finops-hub/modules/fx/hub-app.bicep @@ -372,7 +372,7 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = if (use properties: { ...storageInfrastructureEncryptionProperties supportsHttpsTrafficOnly: true - allowSharedKeyAccess: true + allowSharedKeyAccess: !app.hub.options.disableStorageSharedKeyAccess isHnsEnabled: true minimumTlsVersion: 'TLS1_2' allowBlobPublicAccess: false diff --git a/src/templates/finops-hub/modules/fx/hub-types.bicep b/src/templates/finops-hub/modules/fx/hub-types.bicep index f3727bf0b..ad0b609ef 100644 --- a/src/templates/finops-hub/modules/fx/hub-types.bicep +++ b/src/templates/finops-hub/modules/fx/hub-types.bicep @@ -77,6 +77,7 @@ type HubRoutingProperties = { natGateway: 'Indicates whether a NAT Gateway should be deployed for controlled outbound internet access. When enabled, the script and Data Explorer subnets route outbound traffic through the NAT Gateway.' privateRouting: 'Indicates whether private network routing is enabled.' publisherIsolation: 'Indicates whether FinOps hub resources should be separated by publisher for advanced security.' + disableStorageSharedKeyAccess: 'Indicates whether Shared Key (storage account key) authentication is disabled on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. This does not affect Data Factory, which already authenticates via managed identity and RBAC.' storageInfrastructureEncryption: 'Indicates whether infrastructure encryption is enabled for the storage account.' storageSku: 'Storage account SKU. Allowed values: "Premium_LRS", "Premium_ZRS".' } @@ -102,6 +103,7 @@ type HubProperties = { natGateway: bool privateRouting: bool publisherIsolation: bool + disableStorageSharedKeyAccess: bool storageInfrastructureEncryption: bool storageSku: string } @@ -195,6 +197,7 @@ func newHubInternal( keyVaultSku string, keyVaultEnablePurgeProtection bool, enableInfrastructureEncryption bool, + disableStorageSharedKeyAccess bool, enablePublicAccess bool, enableNatGateway bool, networkName string, @@ -219,6 +222,7 @@ func newHubInternal( natGateway: !enablePublicAccess && enableNatGateway privateRouting: !enablePublicAccess publisherIsolation: false // TODO: Expose publisher isolation option + disableStorageSharedKeyAccess: disableStorageSharedKeyAccess storageInfrastructureEncryption: enableInfrastructureEncryption storageSku: storageSku } @@ -257,6 +261,7 @@ func newHub( keyVaultSku string, keyVaultEnablePurgeProtection bool, enableInfrastructureEncryption bool, + disableStorageSharedKeyAccess bool, enablePublicAccess bool, enableNatGateway bool, networkAddressPrefix string, @@ -272,6 +277,7 @@ func newHub( keyVaultSku, keyVaultEnablePurgeProtection, enableInfrastructureEncryption, + disableStorageSharedKeyAccess, enablePublicAccess, enableNatGateway, '${safeStorageName(name)}-vnet-${location}', // networkName, cSpell:ignore vnet diff --git a/src/templates/finops-hub/modules/hub.bicep b/src/templates/finops-hub/modules/hub.bicep index c5a563d63..52a002449 100644 --- a/src/templates/finops-hub/modules/hub.bicep +++ b/src/templates/finops-hub/modules/hub.bicep @@ -27,6 +27,9 @@ param storageSku string = 'Premium_LRS' @description('Optional. Enable infrastructure encryption on the storage account. Default = false.') param enableInfrastructureEncryption bool = false +@description('Optional. Disable Shared Key (storage account key) authentication on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. Data Factory already authenticates using managed identity and RBAC, so this does not affect data ingestion. Default = false.') +param disableStorageSharedKeyAccess bool = false + @description('Optional. SKU to use for the KeyVault instance, if enabled. Allowed values: "standard", "premium". Default: "premium".') @allowed([ 'premium' @@ -196,6 +199,7 @@ var hub = newHub( keyVaultSku, enablePurgeProtection, enableInfrastructureEncryption, + disableStorageSharedKeyAccess, enablePublicAccess, enableNatGateway, virtualNetworkAddressPrefix, From c4fdd6dfb18a5157aea74129e21e470d8927fd67 Mon Sep 17 00:00:00 2001 From: Michael Flanakin Date: Wed, 28 Oct 2026 22:38:50 -0700 Subject: [PATCH 2/3] fix(hubs): keep Shared Key access on deployment script storage; document remote hub caveat Deployment scripts mount the script storage account in ACI via account key, so allowSharedKeyAccess must stay true there. Remote hub ingestion also uses the key. Co-Authored-By: Claude Sonnet 5.5 --- src/powershell/Public/Deploy-FinOpsHub.ps1 | 2 +- src/templates/finops-hub/createUiDefinition.json | 2 +- src/templates/finops-hub/main.bicep | 2 +- .../modules/Microsoft.FinOpsHubs/Core/infrastructure.bicep | 2 +- src/templates/finops-hub/modules/fx/hub-types.bicep | 2 +- src/templates/finops-hub/modules/hub.bicep | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/powershell/Public/Deploy-FinOpsHub.ps1 b/src/powershell/Public/Deploy-FinOpsHub.ps1 index fad5978cb..46e40494d 100644 --- a/src/powershell/Public/Deploy-FinOpsHub.ps1 +++ b/src/powershell/Public/Deploy-FinOpsHub.ps1 @@ -35,7 +35,7 @@ Optional. Enable purge protection for the Key Vault. Default = false. .PARAMETER DisableStorageSharedKeyAccess - Optional. Disable Shared Key (storage account key) authentication on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. Data Factory already authenticates using managed identity and RBAC, so this does not affect data ingestion. Default = false. + Optional. Disable Shared Key (storage account key) authentication on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. Data Factory already authenticates using managed identity and RBAC, so this does not affect data ingestion. Do not enable this on a hub that receives data from other hubs (remote hub ingestion authenticates with the storage account key). The private-mode deployment script storage account always allows Shared Key access because deployment scripts require it. Default = false. .PARAMETER RemoteHubStorageUri Optional. Storage account to push data to for ingestion into a remote hub. diff --git a/src/templates/finops-hub/createUiDefinition.json b/src/templates/finops-hub/createUiDefinition.json index efe162655..d5c8d4e65 100644 --- a/src/templates/finops-hub/createUiDefinition.json +++ b/src/templates/finops-hub/createUiDefinition.json @@ -888,7 +888,7 @@ "type": "Microsoft.Common.TextBlock", "visible": true, "options": { - "text": "Data Factory always connects to hub storage accounts using its managed identity, so disabling Shared Key access does not affect data ingestion. This can be changed after deployment." + "text": "Data Factory always connects to hub storage accounts using its managed identity, so disabling Shared Key access does not affect data ingestion. Do not enable this on a hub that receives data from other hubs. This can be changed after deployment." } }, { diff --git a/src/templates/finops-hub/main.bicep b/src/templates/finops-hub/main.bicep index c82ec1ffd..cf88e72b9 100644 --- a/src/templates/finops-hub/main.bicep +++ b/src/templates/finops-hub/main.bicep @@ -26,7 +26,7 @@ param storageSku string = 'Premium_LRS' @description('Optional. Enable infrastructure encryption on the storage account. Default = false.') param enableInfrastructureEncryption bool = false -@description('Optional. Disable Shared Key (storage account key) authentication on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. Data Factory already authenticates using managed identity and RBAC, so this does not affect data ingestion. Default = false.') +@description('Optional. Disable Shared Key (storage account key) authentication on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. Data Factory already authenticates using managed identity and RBAC, so this does not affect data ingestion. Do not enable this on a hub that receives data from other hubs (remote hub ingestion authenticates with the storage account key). The private-mode deployment script storage account always allows Shared Key access because deployment scripts require it. Default = false.') param disableStorageSharedKeyAccess bool = false @description('Optional. Enable purge protection for the Key Vault. Default: false.') diff --git a/src/templates/finops-hub/modules/Microsoft.FinOpsHubs/Core/infrastructure.bicep b/src/templates/finops-hub/modules/Microsoft.FinOpsHubs/Core/infrastructure.bicep index f62351ff4..12ec98118 100644 --- a/src/templates/finops-hub/modules/Microsoft.FinOpsHubs/Core/infrastructure.bicep +++ b/src/templates/finops-hub/modules/Microsoft.FinOpsHubs/Core/infrastructure.bicep @@ -378,7 +378,7 @@ resource scriptStorageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = i tags: getHubTags(hub, 'Microsoft.Storage/storageAccounts') properties: { supportsHttpsTrafficOnly: true - allowSharedKeyAccess: !hub.options.disableStorageSharedKeyAccess + allowSharedKeyAccess: true // Required: deployment scripts mount this storage account in Azure Container Instances using the account key. See https://learn.microsoft.com/azure/azure-resource-manager/bicep/deployment-script-develop#use-an-existing-storage-account isHnsEnabled: false minimumTlsVersion: 'TLS1_2' allowBlobPublicAccess: false diff --git a/src/templates/finops-hub/modules/fx/hub-types.bicep b/src/templates/finops-hub/modules/fx/hub-types.bicep index ad0b609ef..ce514d138 100644 --- a/src/templates/finops-hub/modules/fx/hub-types.bicep +++ b/src/templates/finops-hub/modules/fx/hub-types.bicep @@ -77,7 +77,7 @@ type HubRoutingProperties = { natGateway: 'Indicates whether a NAT Gateway should be deployed for controlled outbound internet access. When enabled, the script and Data Explorer subnets route outbound traffic through the NAT Gateway.' privateRouting: 'Indicates whether private network routing is enabled.' publisherIsolation: 'Indicates whether FinOps hub resources should be separated by publisher for advanced security.' - disableStorageSharedKeyAccess: 'Indicates whether Shared Key (storage account key) authentication is disabled on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. This does not affect Data Factory, which already authenticates via managed identity and RBAC.' + disableStorageSharedKeyAccess: 'Indicates whether Shared Key (storage account key) authentication is disabled on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. This does not affect Data Factory, which already authenticates via managed identity and RBAC. Does not apply to the private-mode deployment script storage account, which requires Shared Key access.' storageInfrastructureEncryption: 'Indicates whether infrastructure encryption is enabled for the storage account.' storageSku: 'Storage account SKU. Allowed values: "Premium_LRS", "Premium_ZRS".' } diff --git a/src/templates/finops-hub/modules/hub.bicep b/src/templates/finops-hub/modules/hub.bicep index 32f9d9d0b..4eb84f2a1 100644 --- a/src/templates/finops-hub/modules/hub.bicep +++ b/src/templates/finops-hub/modules/hub.bicep @@ -27,7 +27,7 @@ param storageSku string = 'Premium_LRS' @description('Optional. Enable infrastructure encryption on the storage account. Default = false.') param enableInfrastructureEncryption bool = false -@description('Optional. Disable Shared Key (storage account key) authentication on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. Data Factory already authenticates using managed identity and RBAC, so this does not affect data ingestion. Default = false.') +@description('Optional. Disable Shared Key (storage account key) authentication on hub storage accounts. When enabled, only Microsoft Entra ID and managed identity authentication are allowed. Data Factory already authenticates using managed identity and RBAC, so this does not affect data ingestion. Do not enable this on a hub that receives data from other hubs (remote hub ingestion authenticates with the storage account key). The private-mode deployment script storage account always allows Shared Key access because deployment scripts require it. Default = false.') param disableStorageSharedKeyAccess bool = false @description('Optional. SKU to use for the KeyVault instance, if enabled. Allowed values: "standard", "premium". Default: "premium".') From 2c626b571e6677f915ea67844e85694b2dedd498 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 29 Oct 2026 05:39:26 +0000 Subject: [PATCH 3/3] chore: Update ms.date in docs-mslearn files --- docs-mslearn/toolkit/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-mslearn/toolkit/changelog.md b/docs-mslearn/toolkit/changelog.md index e92eb9786..a6a4b3af8 100644 --- a/docs-mslearn/toolkit/changelog.md +++ b/docs-mslearn/toolkit/changelog.md @@ -3,7 +3,7 @@ title: FinOps toolkit changelog description: Review the latest features and enhancements in the FinOps toolkit, including updates to FinOps hubs, Power BI reports, and more. author: MSBrett ms.author: brettwil -ms.date: 10/28/2026 +ms.date: 10/29/2026 ms.topic: reference ms.service: finops ms.subservice: finops-toolkit