Public/Deploy-FinOpsHub.ps1
# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. <# .SYNOPSIS Deploys a FinOps hub instance. .DESCRIPTION The Deploy-FinOpsHub command either creates a new or updates an existing FinOps hub instance by deploying an Azure Resource Manager deployment template. The FinOps hub template is downloaded from GitHub. Deploy-FinOpsHub calls Initialize-FinOpsHubDeployment before deploying the template. .PARAMETER Name Required. Name of the hub. Used to ensure unique resource names. .PARAMETER ResourceGroupName Required. Name of the resource group to deploy to. Will be created if it doesn't exist. .PARAMETER Location Required. Azure location where all resources should be created. See https://aka.ms/azureregions. .PARAMETER Version Optional. Version of the FinOps hub template to use. Default = "latest". .PARAMETER Preview Optional. Indicates that preview releases should also be included. Default = false. .PARAMETER StorageSku Optional. Storage SKU to use. LRS = Lowest cost, ZRS = High availability. Note Standard SKUs are not available for Data Lake gen2 storage. Allowed: Premium_LRS, Premium_ZRS. Default: Premium_LRS. .PARAMETER EnableInfrastructureEncryption Optional. Enable infrastructure encryption on the storage account. Default = false. .PARAMETER RemoteHubStorageUri Optional. Storage account to push data to for ingestion into a remote hub. .PARAMETER RemoteHubStorageKey Optional. Storage account key to use when pushing data to a remote hub. .PARAMETER DataExplorerName Optional. Name of the Azure Data Explorer cluster to use for advanced analytics. If empty, Azure Data Explorer will not be deployed. Required to use with Power BI if you have more than $2-5M/mo in costs being monitored. Default: "" (do not use). .PARAMETER DataExplorerSku Optional. Name of the Azure Data Explorer SKU. Default: "Dev(No SLA)_Standard_E2a_v4". .PARAMETER DataExplorerCapacity Optional. Number of nodes to use in the cluster. Allowed values: 1 for the Basic SKU tier and 2-1000 for Standard. Default: 1 for dev/test SKUs, 2 for standard SKUs. .PARAMETER Tags Optional. Tags to apply to all resources. We will also add the cm-resource-parent tag for improved cost roll-ups in Cost Management. .PARAMETER TagsByResource Optional. Tags to apply to resources based on their resource type. Resource type specific tags will be merged with tags for all resources. # .PARAMETER ScopesToMonitor # Optional. List of scope IDs to monitor and ingest cost for. # .PARAMETER ExportRetentionInDays # Optional. Number of days of data to retain in the msexports container. Default: 0. # .PARAMETER IngestionRetentionInMonths # Optional. Number of months of data to retain in the ingestion container. Default: 13. .PARAMETER DataExplorerRawRetentionInDays int = 0 Optional. Number of days of data to retain in the Data Explorer *_raw tables. Default: 0. .PARAMETER DataExplorerFinalRetentionInMonths Optional. Number of months of data to retain in the Data Explorer *_final_v* tables. Default: 13. .PARAMETER EnablePublicAccess Optional. Enable public access to the data lake. Default: true. .PARAMETER VirtualNetworkAddressPrefix Optional. Address space for the workload. A /26 is required for the workload. Default: "10.20.30.0/26". .EXAMPLE Deploy-FinOpsHub -Name MyHub -ResourceGroupName MyNewResourceGroup -Location westus -DataExplorerName MyFinOpsHubCluster Deploys a FinOps hub instance named MyHub to the MyNewResourceGroup resource group with a new MyFinOpsHubCluster Data Explorer cluster. If the resource group does not exist, it will be created. If the hub already exists, it will be updated to the latest version. .EXAMPLE Deploy-FinOpsHub -Name MyHub -ResourceGroupName MyExistingResourceGroup -Location westus -Version 0.1.1 Deploys a FinOps hub instance named MyHub to the MyExistingResourceGroup resource group using version 0.1.1 of the template. This version is required in order to deploy to Azure Gov or Azure China as of February 2024 since FOCUS exports are not available from Cost Management in those environments. If the resource group does not exist, it will be created. If the hub already exists, it will be updated to version 0.1.1. .LINK https://aka.ms/ftk/Deploy-FinOpsHub #> function Deploy-FinOpsHub { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "")] [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $Name, [Parameter(Mandatory = $true)] [string] $ResourceGroupName, [Parameter(Mandatory = $true)] [string] $Location, [Parameter()] [string] $Version = 'latest', [Parameter()] [switch] $Preview, [Parameter()] [ValidateSet('Premium_LRS', 'Premium_ZRS')] [string] $StorageSku = 'Premium_LRS', [Parameter()] [switch] $EnableInfrastructureEncryption, [Parameter()] [string] $RemoteHubStorageUri, [Parameter()] [string] $RemoteHubStorageKey, [Parameter()] [string] $DataExplorerName, [Parameter()] [ValidateSet('Dev(No SLA)_Standard_E2a_v4', 'Dev(No SLA)_Standard_D11_v2', 'Standard_D11_v2', 'Standard_D12_v2', 'Standard_D13_v2', 'Standard_D14_v2', 'Standard_D16d_v5', 'Standard_D32d_v4', 'Standard_D32d_v5', 'Standard_DS13_v2+1TB_PS', 'Standard_DS13_v2+2TB_PS', 'Standard_DS14_v2+3TB_PS', 'Standard_DS14_v2+4TB_PS', 'Standard_E2a_v4', 'Standard_E2ads_v5', 'Standard_E2d_v4', 'Standard_E2d_v5', 'Standard_E4a_v4', 'Standard_E4ads_v5', 'Standard_E4d_v4', 'Standard_E4d_v5', 'Standard_E8a_v4', 'Standard_E8ads_v5', 'Standard_E8as_v4+1TB_PS', 'Standard_E8as_v4+2TB_PS', 'Standard_E8as_v5+1TB_PS', 'Standard_E8as_v5+2TB_PS', 'Standard_E8d_v4', 'Standard_E8d_v5', 'Standard_E8s_v4+1TB_PS', 'Standard_E8s_v4+2TB_PS', 'Standard_E8s_v5+1TB_PS', 'Standard_E8s_v5+2TB_PS', 'Standard_E16a_v4', 'Standard_E16ads_v5', 'Standard_E16as_v4+3TB_PS', 'Standard_E16as_v4+4TB_PS', 'Standard_E16as_v5+3TB_PS', 'Standard_E16as_v5+4TB_PS', 'Standard_E16d_v4', 'Standard_E16d_v5', 'Standard_E16s_v4+3TB_PS', 'Standard_E16s_v4+4TB_PS', 'Standard_E16s_v5+3TB_PS', 'Standard_E16s_v5+4TB_PS', 'Standard_E64i_v3', 'Standard_E80ids_v4', 'Standard_EC8ads_v5', 'Standard_EC8as_v5+1TB_PS', 'Standard_EC8as_v5+2TB_PS', 'Standard_EC16ads_v5', 'Standard_EC16as_v5+3TB_PS', 'Standard_EC16as_v5+4TB_PS', 'Standard_L4s', 'Standard_L8as_v3', 'Standard_L8s', 'Standard_L8s_v2', 'Standard_L8s_v3', 'Standard_L16as_v3', 'Standard_L16s', 'Standard_L16s_v2', 'Standard_L16s_v3', 'Standard_L32as_v3', 'Standard_L32s_v3')] [string] $DataExplorerSku = 'Dev(No SLA)_Standard_D11_v2', [Parameter()] [ValidateRange(1, 1000)] [int] $DataExplorerCapacity = 1, [Parameter()] [ValidateRange(0, 9999)] [int] $DataExplorerRawRetentionInDays = 0, [Parameter()] [ValidateRange(0, 999)] [int] $DataExplorerFinalRetentionInMonths = 13, [Parameter()] [switch] $DisablePublicAccess, [Parameter()] [string] $VirtualNetworkAddressPrefix = '10.20.30.0/26', [Parameter()] [hashtable] $Tags ) try { # Create resource group if it doesn't exist $resourceGroupObject = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction 'SilentlyContinue' if (-not $resourceGroupObject -and (Test-ShouldProcess $PSCmdlet $ResourceGroupName 'CreateResourceGroup')) { $resourceGroupObject = New-AzResourceGroup -Name $ResourceGroupName -Location $Location } # Create folder for download $toolkitPath = Join-Path $env:temp -ChildPath 'FinOpsToolkit' if (Test-ShouldProcess $PSCmdlet $toolkitPath 'CreateTempDirectory') { New-Directory -Path $toolkitPath } # Init deployment (register providers) Initialize-FinOpsHubDeployment -WhatIf:$WhatIfPreference # Download template if (Test-ShouldProcess $PSCmdlet $Version 'DownloadTemplate') { Save-FinOpsHubTemplate -Version $Version -Preview:$Preview -Destination $toolkitPath $bicepFile = Get-ChildItem -Path $toolkitPath -Include 'main.bicep' -Recurse | Where-Object -FilterScript { $_.FullName -like '*finops-hub-v*' } if (-not $bicepFile) { throw ($LocalizedData.Hub_Deploy_TemplateNotFound -f $toolkitPath) } $parameterSplat = @{ TemplateFile = $bicepFile.FullName TemplateParameterObject = @{ hubName = $Name storageSku = $StorageSku } } if ($Version -eq 'latest' -or [version]$Version -ge '0.4') { $parameterSplat.TemplateParameterObject.Add('remoteHubStorageUri', $RemoteHubStorageUri) $parameterSplat.TemplateParameterObject.Add('remoteHubStorageKey', $RemoteHubStorageKey) } if ($Version -eq 'latest' -or [version]$Version -ge '0.7') { $parameterSplat.TemplateParameterObject.Add('enableInfrastructureEncryption', $EnableInfrastructureEncryption) $parameterSplat.TemplateParameterObject.Add('dataExplorerName', $DataExplorerName) $parameterSplat.TemplateParameterObject.Add('dataExplorerSku', $DataExplorerSku) $parameterSplat.TemplateParameterObject.Add('dataExplorerCapacity', $DataExplorerCapacity) $parameterSplat.TemplateParameterObject.Add('dataExplorerRawRetentionInDays', $DataExplorerRawRetentionInDays) $parameterSplat.TemplateParameterObject.Add('dataExplorerFinalRetentionInMonths', $DataExplorerFinalRetentionInMonths) $parameterSplat.TemplateParameterObject.Add('enablePublicAccess', -not $DisablePublicAccess) $parameterSplat.TemplateParameterObject.Add('virtualNetworkAddressPrefix', $VirtualNetworkAddressPrefix) } if ($Tags -and $Tags.Keys.Count -gt 0) { $parameterSplat.TemplateParameterObject.Add('tags', $Tags) } } # Run the deployment if (Test-ShouldProcess $PSCmdlet $ResourceGroupName 'DeployFinOpsHub') { Write-Verbose -Message ($LocalizedData.Hub_Deploy_Deploy -f $bicepFile.FullName, $resourceGroupObject.ResourceGroupName) return New-AzResourceGroupDeployment @parameterSplat -ResourceGroupName $resourceGroupObject.ResourceGroupName } } catch { throw $_.Exception.Message } finally { # Clean up downloaded files Remove-Item -Path $toolkitPath -Recurse -Force -ErrorAction 'SilentlyContinue' } } |