From 5a4aa4ae91fdef88a517009f688da676ae06b4a1 Mon Sep 17 00:00:00 2001 From: Michael Flanakin Date: Mon, 5 Feb 2024 02:23:56 -0800 Subject: [PATCH] Update version + publish release --- docs/_includes/version.txt | 2 +- docs/deploy/finops-hub-0.2.1-rc.3.json | 2138 ++++++++++++++++++++++++ docs/deploy/finops-hub-latest.json | 119 +- package-lock.json | 4 +- package.json | 2 +- src/scripts/Package-Toolkit.ps1 | 13 +- 6 files changed, 2235 insertions(+), 43 deletions(-) create mode 100644 docs/deploy/finops-hub-0.2.1-rc.3.json diff --git a/docs/_includes/version.txt b/docs/_includes/version.txt index 70e8e888c..0dd8f7c41 100644 --- a/docs/_includes/version.txt +++ b/docs/_includes/version.txt @@ -1 +1 @@ -0.2.1-rc.2 \ No newline at end of file +0.2.1-rc.3 \ No newline at end of file diff --git a/docs/deploy/finops-hub-0.2.1-rc.3.json b/docs/deploy/finops-hub-0.2.1-rc.3.json new file mode 100644 index 000000000..0e32b0cd5 --- /dev/null +++ b/docs/deploy/finops-hub-0.2.1-rc.3.json @@ -0,0 +1,2138 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "9748975281421053488" + } + }, + "parameters": { + "hubName": { + "type": "string", + "metadata": { + "description": "Optional. Name of the hub. Used to ensure unique resource names. Default: \"finops-hub\"." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Azure location where all resources should be created. See https://aka.ms/azureregions. Default: Same as deployment." + } + }, + "storageSku": { + "type": "string", + "defaultValue": "Premium_LRS", + "allowedValues": [ + "Premium_LRS", + "Premium_ZRS" + ], + "metadata": { + "description": "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." + } + }, + "existingKeyVaultId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to all resources. We will also add the cm-resource-parent tag for improved cost roll-ups in Cost Management." + } + }, + "tagsByResource": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to resources based on their resource type. Resource type specific tags will be merged with tags for all resources." + } + }, + "exportScopes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of scope IDs to create exports for." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "hub", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "hubName": { + "value": "[parameters('hubName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "storageSku": { + "value": "[parameters('storageSku')]" + }, + "existingKeyVaultId": { + "value": "[parameters('existingKeyVaultId')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "tagsByResource": { + "value": "[parameters('tagsByResource')]" + }, + "exportScopes": { + "value": "[parameters('exportScopes')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "17411969862525330364" + } + }, + "parameters": { + "hubName": { + "type": "string", + "metadata": { + "description": "Optional. Name of the hub. Used to ensure unique resource names. Default: \"finops-hub\"." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Azure location where all resources should be created. See https://aka.ms/azureregions. Default: (resource group location)." + } + }, + "storageSku": { + "type": "string", + "defaultValue": "Premium_LRS", + "allowedValues": [ + "Premium_LRS", + "Premium_ZRS" + ], + "metadata": { + "description": "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." + } + }, + "existingKeyVaultId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to all resources. We will also add the cm-resource-parent tag for improved cost roll-ups in Cost Management." + } + }, + "tagsByResource": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to resources based on their resource type. Resource type specific tags will be merged with tags for all resources." + } + }, + "exportScopes": { + "type": "array", + "metadata": { + "description": "Optional. List of scope IDs to create exports for." + } + }, + "convertToParquet": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether ingested data should be converted to Parquet. Default: true." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry to track anonymous module usage trends, monitor for bugs, and improve future releases." + } + } + }, + "variables": { + "resourceTags": "[union(parameters('tags'), createObject('cm-resource-parent', format('{0}/providers/Microsoft.Cloud/hubs/{1}', resourceGroup().id, parameters('hubName'))))]", + "uniqueSuffix": "[uniqueString(parameters('hubName'), resourceGroup().id)]", + "dataFactoryPrefix": "[format('{0}-engine', replace(parameters('hubName'), '_', '-'))]", + "dataFactorySuffix": "[format('-{0}', variables('uniqueSuffix'))]", + "dataFactoryName": "[replace(format('{0}{1}', take(variables('dataFactoryPrefix'), sub(63, length(variables('dataFactorySuffix')))), variables('dataFactorySuffix')), '--', '-')]", + "telemetryId": "00f120b5-2007-6120-0000-40b000000000", + "finOpsToolkitVersion": "placeholder" + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('pid-{0}-{1}', variables('telemetryId'), uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "FinOps toolkit", + "version": "[variables('finOpsToolkitVersion')]" + } + }, + "resources": [] + } + } + }, + { + "type": "Microsoft.DataFactory/factories", + "apiVersion": "2018-06-01", + "name": "[variables('dataFactoryName')]", + "location": "[parameters('location')]", + "tags": "[union(variables('resourceTags'), if(contains(parameters('tagsByResource'), 'Microsoft.DataFactory/factories'), parameters('tagsByResource')['Microsoft.DataFactory/factories'], createObject()))]", + "identity": { + "type": "SystemAssigned" + }, + "properties": "[union(createObject(), createObject('globalConfigurations', createObject('PipelineBillingEnabled', 'true')))]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "storage", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "hubName": { + "value": "[parameters('hubName')]" + }, + "uniqueSuffix": { + "value": "[variables('uniqueSuffix')]" + }, + "sku": { + "value": "[parameters('storageSku')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('resourceTags')]" + }, + "tagsByResource": { + "value": "[parameters('tagsByResource')]" + }, + "exportScopes": { + "value": "[parameters('exportScopes')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "9609675916139055971" + } + }, + "parameters": { + "hubName": { + "type": "string", + "metadata": { + "description": "Required. Name of the hub. Used to ensure unique resource names." + } + }, + "uniqueSuffix": { + "type": "string", + "metadata": { + "description": "Required. Suffix to add to the storage account name to ensure uniqueness." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Azure location where all resources should be created. See https://aka.ms/azureregions. Default: (resource group location)." + } + }, + "sku": { + "type": "string", + "defaultValue": "Premium_LRS", + "allowedValues": [ + "Premium_LRS", + "Premium_ZRS" + ], + "metadata": { + "description": "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." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to all resources. We will also add the cm-resource-parent tag for improved cost roll-ups in Cost Management." + } + }, + "tagsByResource": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to resources based on their resource type. Resource type specific tags will be merged with tags for all resources." + } + }, + "exportScopes": { + "type": "array", + "metadata": { + "description": "Optional. List of scope IDs to create exports for." + } + } + }, + "variables": { + "$fxv#0": "placeholder", + "$fxv#1": "# Copyright (c) Microsoft Corporation.\r\n# Licensed under the MIT License.\r\n\r\nWrite-Output \"Updating settings.json file...\"\r\nWrite-Output \" Storage account: $env:storageAccountName\"\r\nWrite-Output \" Container: $env:containerName\"\r\n\r\n$validateScopes = { $_.Length -gt 45 }\r\n\r\n# Initialize variables\r\n$fileName = 'settings.json'\r\n$filePath = Join-Path -Path . -ChildPath $fileName\r\n$newScopes = $env:exportScopes.Split('|') | Where-Object $validateScopes | ForEach-Object { @{ scope = $_ } }\r\n\r\n# Get storage context\r\n$storageContext = @{\r\n Context = New-AzStorageContext -StorageAccountName $env:storageAccountName -UseConnectedAccount\r\n Container = $env:containerName\r\n}\r\n\r\n# Download existing settings, if they exist\r\n$blob = Get-AzStorageBlobContent @storageContext -Blob $fileName -Destination $filePath -Force\r\nif ($blob) {\r\n Write-Output \"Existing settings.json file found. Updating...\"\r\n $text = Get-Content $filePath -Raw\r\n Write-Output \"---------\"\r\n Write-Output $text\r\n Write-Output \"---------\"\r\n $json = $text | ConvertFrom-Json\r\n\r\n # Rename exportScopes to scopes + convert to object array\r\n if ($json.exportScopes) {\r\n Write-Output \" Updating exportScopes...\"\r\n if ($json.exportScopes[0] -is [string]) {\r\n Write-Output \" Converting string array to object array...\"\r\n $json.exportScopes = $json.exportScopes | Where-Object $validateScopes | ForEach-Object { @{ scope = $_ } }\r\n if (-not ($json.exportScopes -is [array])) {\r\n Write-Output \" Converting single object to object array...\"\r\n $json.exportScopes = @($json.exportScopes)\r\n }\r\n }\r\n\r\n Write-Output \" Renaming to 'scopes'...\"\r\n $json | Add-Member -MemberType NoteProperty -Name scopes -Value $json.exportScopes\r\n $json.PSObject.Properties.Remove('exportScopes')\r\n }\r\n}\r\n\r\n# Set default if not found\r\nif (!$json) {\r\n Write-Output \"No existing settings.json file found. Creating new file...\"\r\n $json = [ordered]@{\r\n '$schema' = 'https://aka.ms/finops/hubs/settings-schema'\r\n type = 'HubInstance'\r\n version = ''\r\n learnMore = 'https://aka.ms/finops/hubs'\r\n scopes = @()\r\n }\r\n}\r\n\r\n# Updating settings\r\nWrite-Output \"Updating version to $env:ftkVersion...\"\r\n$json.version = $env:ftkVersion\r\nif ($newScopes) {\r\n Write-Output \"Merging $($newScopes.Count) scopes...\"\r\n $json.scopes = Compare-Object -ReferenceObject $json.scopes -DifferenceObject $newScopes -Property scope -PassThru -IncludeEqual\r\n\r\n # Remove the SideIndicator property from the Compare-Object output\r\n $json.scopes | ForEach-Object { $_.PSObject.Properties.Remove('SideIndicator') } | ConvertTo-Json\r\n\r\n if (-not ($json.scopes -is [array])) {\r\n $json.scopes = @($json.scopes)\r\n }\r\n Write-Output \"$($json.scopes.Count) scopes found.\"\r\n}\r\n$text = $json | ConvertTo-Json\r\nWrite-Output \"---------\"\r\nWrite-Output $text\r\nWrite-Output \"---------\"\r\n$text | Out-File $filePath\r\n\r\n# Upload new/updated settings\r\nWrite-Output \"Uploading settings.json file...\"\r\nSet-AzStorageBlobContent @storageContext -File $filePath -Force\r\n", + "safeHubName": "[replace(replace(toLower(parameters('hubName')), '-', ''), '_', '')]", + "storageAccountSuffix": "[parameters('uniqueSuffix')]", + "storageAccountName": "[format('{0}{1}', take(variables('safeHubName'), sub(24, length(variables('storageAccountSuffix')))), variables('storageAccountSuffix'))]", + "blobUploadRbacRoles": [ + "ba92f5b4-2d11-453d-a403-e96b0029c9fe", + "e40ec5ca-96e0-45a2-b4ff-59039f2c2b59" + ] + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-08-01", + "name": "[variables('storageAccountName')]", + "location": "[parameters('location')]", + "sku": { + "name": "[parameters('sku')]" + }, + "kind": "BlockBlobStorage", + "tags": "[union(parameters('tags'), if(contains(parameters('tagsByResource'), 'Microsoft.Storage/storageAccounts'), parameters('tagsByResource')['Microsoft.Storage/storageAccounts'], createObject()))]", + "properties": { + "supportsHttpsTrafficOnly": true, + "isHnsEnabled": true, + "minimumTlsVersion": "TLS1_2", + "allowBlobPublicAccess": false + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', variables('storageAccountName'), 'default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}/{2}', variables('storageAccountName'), 'default', 'config')]", + "properties": { + "publicAccess": "None", + "metadata": {} + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccountName'), 'default')]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}/{2}', variables('storageAccountName'), 'default', 'msexports')]", + "properties": { + "publicAccess": "None", + "metadata": {} + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccountName'), 'default')]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}/{2}', variables('storageAccountName'), 'default', 'ingestion')]", + "properties": { + "publicAccess": "None", + "metadata": {} + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccountName'), 'default')]" + ] + }, + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[format('{0}_blobManager', variables('storageAccountName'))]", + "tags": "[union(parameters('tags'), if(contains(parameters('tagsByResource'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), parameters('tagsByResource')['Microsoft.ManagedIdentity/userAssignedIdentities'], createObject()))]", + "location": "[parameters('location')]" + }, + { + "copy": { + "name": "identityRoleAssignments", + "count": "[length(variables('blobUploadRbacRoles'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), variables('blobUploadRbacRoles')[copyIndex()], resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_blobManager', variables('storageAccountName'))))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', variables('blobUploadRbacRoles')[copyIndex()])]", + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_blobManager', variables('storageAccountName'))), '2023-01-31').principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_blobManager', variables('storageAccountName')))]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" + ] + }, + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "uploadSettings", + "kind": "AzurePowerShell", + "location": "[if(startsWith(parameters('location'), 'china'), 'chinaeast2', parameters('location'))]", + "tags": "[union(parameters('tags'), if(contains(parameters('tagsByResource'), 'Microsoft.Resources/deploymentScripts'), parameters('tagsByResource')['Microsoft.Resources/deploymentScripts'], createObject()))]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_blobManager', variables('storageAccountName'))))]": {} + } + }, + "properties": { + "azPowerShellVersion": "8.0", + "retentionInterval": "PT1H", + "environmentVariables": [ + { + "name": "ftkVersion", + "value": "[variables('$fxv#0')]" + }, + { + "name": "exportScopes", + "value": "[join(parameters('exportScopes'), '|')]" + }, + { + "name": "storageAccountName", + "value": "[variables('storageAccountName')]" + }, + { + "name": "containerName", + "value": "config" + } + ], + "scriptContent": "[variables('$fxv#1')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', variables('storageAccountName'), 'default', 'config')]", + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_blobManager', variables('storageAccountName')))]", + "identityRoleAssignments" + ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the storage account." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the storage account." + }, + "value": "[variables('storageAccountName')]" + }, + "configContainer": { + "type": "string", + "metadata": { + "description": "The name of the container used for configuration settings." + }, + "value": "config" + }, + "exportContainer": { + "type": "string", + "metadata": { + "description": "The name of the container used for Cost Management exports." + }, + "value": "msexports" + }, + "ingestionContainer": { + "type": "string", + "metadata": { + "description": "The name of the container used for normalized data ingestion." + }, + "value": "ingestion" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "dataFactoryResources", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "dataFactoryName": { + "value": "[variables('dataFactoryName')]" + }, + "convertToParquet": { + "value": "[parameters('convertToParquet')]" + }, + "keyVaultId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyVault'), '2022-09-01').outputs.resourceId.value]" + }, + "storageAccountName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value]" + }, + "exportContainerName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.exportContainer.value]" + }, + "ingestionContainerName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.ingestionContainer.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('resourceTags')]" + }, + "tagsByResource": { + "value": "[parameters('tagsByResource')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "8197671316834274442" + } + }, + "parameters": { + "dataFactoryName": { + "type": "string", + "metadata": { + "description": "Required. Name of the hub. Used to ensure unique resource names." + } + }, + "keyVaultId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the Azure Key Vault instance." + } + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Azure storage account instance." + } + }, + "exportContainerName": { + "type": "string", + "metadata": { + "description": "Required. The name of the container where Cost Management data is exported." + } + }, + "ingestionContainerName": { + "type": "string", + "metadata": { + "description": "Required. The name of the container where normalized data is ingested." + } + }, + "convertToParquet": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether ingested data should be converted to Parquet. Default: true." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location to use for the managed identity and deployment script to auto-start triggers. Default = (resource group location)." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to all resources. We will also add the cm-resource-parent tag for improved cost roll-ups in Cost Management." + } + }, + "tagsByResource": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to resources based on their resource type. Resource type specific tags will be merged with tags for all resources." + } + } + }, + "variables": { + "copy": [ + { + "name": "focusCostMappings", + "count": "[length(range(0, length(variables('focusCostColumns'))))]", + "input": { + "source": { + "name": "[variables('focusCostColumns')[range(0, length(variables('focusCostColumns')))[copyIndex('focusCostMappings')]].name]", + "type": "[variables('focusCostColumns')[range(0, length(variables('focusCostColumns')))[copyIndex('focusCostMappings')]].type]" + }, + "sink": { + "name": "[variables('focusCostColumns')[range(0, length(variables('focusCostColumns')))[copyIndex('focusCostMappings')]].name]" + } + } + } + ], + "$fxv#0": "# Copyright (c) Microsoft Corporation.\r\n# Licensed under the MIT License.\r\n\r\n# Init outputs\r\n$DeploymentScriptOutputs = @{}\r\n\r\n# \r\n$adfParams = @{\r\n ResourceGroupName = $env:DataFactoryResourceGroup\r\n DataFactoryName = $env:DataFactoryName\r\n}\r\n\r\n# Delete old triggers\r\n$triggers = Get-AzDataFactoryV2Trigger @adfParams -ErrorAction SilentlyContinue `\r\n| Where-Object { $_.Name -match '^msexports?$' }\r\n$DeploymentScriptOutputs[\"stopTriggers\"] = $triggers | Stop-AzDataFactoryV2Trigger -Force -ErrorAction SilentlyContinue\r\n$DeploymentScriptOutputs[\"deleteTriggers\"] = $triggers | Remove-AzDataFactoryV2Trigger -Force -ErrorAction SilentlyContinue\r\n\r\n# Delete old pipelines\r\n$DeploymentScriptOutputs[\"pipelines\"] = Get-AzDataFactoryV2Pipeline @adfParams -ErrorAction SilentlyContinue `\r\n| Where-Object { $_.Name -match '^msexports_(extract|transform)$' } `\r\n| Remove-AzDataFactoryV2Pipeline -Force -ErrorAction SilentlyContinue", + "$fxv#1": "# Copyright (c) Microsoft Corporation.\r\n# Licensed under the MIT License.\r\n\r\nParam(\r\n [switch] $Stop\r\n)\r\n\r\n# Init outputs\r\n$DeploymentScriptOutputs = @{}\r\n\r\nif (-not $Stop) {\r\n Start-Sleep -Seconds 10\r\n}\r\n\r\n# Loop thru triggers\r\n$env:Triggers.Split('|') `\r\n| ForEach-Object {\r\n $trigger = $_\r\n if ($Stop) {\r\n Write-Host \"Stopping trigger $trigger...\" -NoNewline\r\n $triggerOutput = Stop-AzDataFactoryV2Trigger `\r\n -ResourceGroupName $env:DataFactoryResourceGroup `\r\n -DataFactoryName $env:DataFactoryName `\r\n -Name $trigger `\r\n -Force `\r\n -ErrorAction SilentlyContinue # Ignore errors, since the trigger may not exist\r\n } else {\r\n Write-Host \"Starting trigger $trigger...\" -NoNewline\r\n $triggerOutput = Start-AzDataFactoryV2Trigger `\r\n -ResourceGroupName $env:DataFactoryResourceGroup `\r\n -DataFactoryName $env:DataFactoryName `\r\n -Name $trigger `\r\n -Force\r\n }\r\n if ($triggerOutput) {\r\n Write-Host 'done'\r\n } else {\r\n Write-Host 'failed'\r\n }\r\n $DeploymentScriptOutputs[$trigger] = $triggerOutput\r\n}\r\n\r\nif ($Stop) {\r\n Start-Sleep -Seconds 10\r\n}\r\n", + "$fxv#2": "# Copyright (c) Microsoft Corporation.\r\n# Licensed under the MIT License.\r\n\r\nParam(\r\n [switch] $Stop\r\n)\r\n\r\n# Init outputs\r\n$DeploymentScriptOutputs = @{}\r\n\r\nif (-not $Stop) {\r\n Start-Sleep -Seconds 10\r\n}\r\n\r\n# Loop thru triggers\r\n$env:Triggers.Split('|') `\r\n| ForEach-Object {\r\n $trigger = $_\r\n if ($Stop) {\r\n Write-Host \"Stopping trigger $trigger...\" -NoNewline\r\n $triggerOutput = Stop-AzDataFactoryV2Trigger `\r\n -ResourceGroupName $env:DataFactoryResourceGroup `\r\n -DataFactoryName $env:DataFactoryName `\r\n -Name $trigger `\r\n -Force `\r\n -ErrorAction SilentlyContinue # Ignore errors, since the trigger may not exist\r\n } else {\r\n Write-Host \"Starting trigger $trigger...\" -NoNewline\r\n $triggerOutput = Start-AzDataFactoryV2Trigger `\r\n -ResourceGroupName $env:DataFactoryResourceGroup `\r\n -DataFactoryName $env:DataFactoryName `\r\n -Name $trigger `\r\n -Force\r\n }\r\n if ($triggerOutput) {\r\n Write-Host 'done'\r\n } else {\r\n Write-Host 'failed'\r\n }\r\n $DeploymentScriptOutputs[$trigger] = $triggerOutput\r\n}\r\n\r\nif ($Stop) {\r\n Start-Sleep -Seconds 10\r\n}\r\n", + "datasetPropsDelimitedText": { + "columnDelimiter": ",", + "compressionLevel": "Optimal", + "escapeChar": "\"", + "firstRowAsHeader": true, + "quoteChar": "\"" + }, + "datasetPropsCommon": { + "location": { + "type": "AzureBlobFSLocation", + "fileName": { + "value": "@{dataset().fileName}", + "type": "Expression" + }, + "folderPath": { + "value": "@{dataset().folderName}", + "type": "Expression" + } + } + }, + "safeExportContainerName": "[replace(format('{0}', parameters('exportContainerName')), '-', '_')]", + "safeIngestionContainerName": "[replace(format('{0}', parameters('ingestionContainerName')), '-', '_')]", + "exportFileAddedTriggerName": "[format('{0}_FileAdded', variables('safeExportContainerName'))]", + "allHubTriggers": [ + "[variables('exportFileAddedTriggerName')]" + ], + "autoStartRbacRoles": [ + "673868aa-7521-48a0-acc6-0f60742d39f5", + "e40ec5ca-96e0-45a2-b4ff-59039f2c2b59" + ], + "focusCostColumns": [ + { + "name": "AvailabilityZone", + "type": "String" + }, + { + "name": "BilledCost", + "type": "Decimal" + }, + { + "name": "BillingAccountId", + "type": "String" + }, + { + "name": "BillingAccountName", + "type": "String" + }, + { + "name": "BillingAccountType", + "type": "String" + }, + { + "name": "BillingCurrency", + "type": "String" + }, + { + "name": "BillingPeriodEnd", + "type": "DateTime" + }, + { + "name": "BillingPeriodStart", + "type": "DateTime" + }, + { + "name": "ChargeCategory", + "type": "String" + }, + { + "name": "ChargeDescription", + "type": "String" + }, + { + "name": "ChargeFrequency", + "type": "String" + }, + { + "name": "ChargePeriodEnd", + "type": "DateTime" + }, + { + "name": "ChargePeriodStart", + "type": "DateTime" + }, + { + "name": "ChargeSubcategory", + "type": "String" + }, + { + "name": "CommitmentDiscountCategory", + "type": "String" + }, + { + "name": "CommitmentDiscountId", + "type": "String" + }, + { + "name": "CommitmentDiscountName", + "type": "String" + }, + { + "name": "CommitmentDiscountType", + "type": "String" + }, + { + "name": "EffectiveCost", + "type": "Decimal" + }, + { + "name": "InvoiceIssuerName", + "type": "String" + }, + { + "name": "ListCost", + "type": "Decimal" + }, + { + "name": "ListUnitPrice", + "type": "Decimal" + }, + { + "name": "PricingCategory", + "type": "String" + }, + { + "name": "PricingQuantity", + "type": "Decimal" + }, + { + "name": "PricingUnit", + "type": "String" + }, + { + "name": "ProviderName", + "type": "String" + }, + { + "name": "PublisherName", + "type": "String" + }, + { + "name": "Region", + "type": "String" + }, + { + "name": "ResourceId", + "type": "String" + }, + { + "name": "ResourceName", + "type": "String" + }, + { + "name": "ResourceType", + "type": "String" + }, + { + "name": "ServiceCategory", + "type": "String" + }, + { + "name": "ServiceName", + "type": "String" + }, + { + "name": "SkuId", + "type": "String" + }, + { + "name": "SkuPriceId", + "type": "String" + }, + { + "name": "SubAccountId", + "type": "String" + }, + { + "name": "SubAccountName", + "type": "String" + }, + { + "name": "SubAccountType", + "type": "String" + }, + { + "name": "Tags", + "type": "String" + }, + { + "name": "UsageQuantity", + "type": "Decimal" + }, + { + "name": "UsageUnit", + "type": "String" + }, + { + "name": "x_AccountName", + "type": "String" + }, + { + "name": "x_AccountOwnerId", + "type": "String" + }, + { + "name": "x_BilledCostInUsd", + "type": "Decimal" + }, + { + "name": "x_BilledUnitPrice", + "type": "Decimal" + }, + { + "name": "x_BillingAccountId", + "type": "String" + }, + { + "name": "x_BillingAccountName", + "type": "String" + }, + { + "name": "x_BillingExchangeRate", + "type": "Decimal" + }, + { + "name": "x_BillingExchangeRateDate", + "type": "DateTime" + }, + { + "name": "x_BillingProfileId", + "type": "String" + }, + { + "name": "x_BillingProfileName", + "type": "String" + }, + { + "name": "x_ChargeId", + "type": "String" + }, + { + "name": "x_CostAllocationRuleName", + "type": "String" + }, + { + "name": "x_CostCenter", + "type": "String" + }, + { + "name": "x_CustomerId", + "type": "String" + }, + { + "name": "x_CustomerName", + "type": "String" + }, + { + "name": "x_EffectiveCostInUsd", + "type": "Decimal" + }, + { + "name": "x_EffectiveUnitPrice", + "type": "Decimal" + }, + { + "name": "x_InvoiceId", + "type": "String" + }, + { + "name": "x_InvoiceIssuerId", + "type": "String" + }, + { + "name": "x_InvoiceSectionId", + "type": "String" + }, + { + "name": "x_InvoiceSectionName", + "type": "String" + }, + { + "name": "x_OnDemandCost", + "type": "Decimal" + }, + { + "name": "x_OnDemandCostInUsd", + "type": "Decimal" + }, + { + "name": "x_OnDemandUnitPrice", + "type": "Decimal" + }, + { + "name": "x_PartnerCreditApplied", + "type": "Boolean" + }, + { + "name": "x_PartnerCreditRate", + "type": "Decimal" + }, + { + "name": "x_PricingBlockSize", + "type": "Decimal" + }, + { + "name": "x_PricingCurrency", + "type": "String" + }, + { + "name": "x_PricingSubcategory", + "type": "String" + }, + { + "name": "x_PricingUnitDescription", + "type": "String" + }, + { + "name": "x_PublisherCategory", + "type": "String" + }, + { + "name": "x_PublisherId", + "type": "String" + }, + { + "name": "x_ResellerId", + "type": "String" + }, + { + "name": "x_ResellerName", + "type": "String" + }, + { + "name": "x_ResourceGroupName", + "type": "String" + }, + { + "name": "x_ResourceType", + "type": "String" + }, + { + "name": "x_ServicePeriodEnd", + "type": "DateTime" + }, + { + "name": "x_ServicePeriodStart", + "type": "DateTime" + }, + { + "name": "x_SkuDescription", + "type": "String" + }, + { + "name": "x_SkuDetails", + "type": "String" + }, + { + "name": "x_SkuIsCreditEligible", + "type": "Boolean" + }, + { + "name": "x_SkuMeterCategory", + "type": "String" + }, + { + "name": "x_SkuMeterId", + "type": "String" + }, + { + "name": "x_SkuMeterName", + "type": "String" + }, + { + "name": "x_SkuMeterSubcategory", + "type": "String" + }, + { + "name": "x_SkuOfferId", + "type": "String" + }, + { + "name": "x_SkuOrderId", + "type": "String" + }, + { + "name": "x_SkuOrderName", + "type": "String" + }, + { + "name": "x_SkuPartNumber", + "type": "String" + }, + { + "name": "x_SkuRegion", + "type": "String" + }, + { + "name": "x_SkuServiceFamily", + "type": "String" + }, + { + "name": "x_SkuTerm", + "type": "String" + }, + { + "name": "x_SkuTier", + "type": "String" + } + ] + }, + "resources": [ + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('{0}_deleteOldResources', parameters('dataFactoryName'))]", + "location": "[parameters('location')]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_triggerManager', parameters('dataFactoryName'))))]": {} + } + }, + "kind": "AzurePowerShell", + "tags": "[parameters('tags')]", + "properties": { + "azPowerShellVersion": "8.0", + "retentionInterval": "PT1H", + "cleanupPreference": "OnSuccess", + "scriptContent": "[variables('$fxv#0')]", + "environmentVariables": [ + { + "name": "DataFactorySubscriptionId", + "value": "[subscription().id]" + }, + { + "name": "DataFactoryResourceGroup", + "value": "[resourceGroup().name]" + }, + { + "name": "DataFactoryName", + "value": "[parameters('dataFactoryName')]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_triggerManager', parameters('dataFactoryName')))]", + "identityRoleAssignments" + ] + }, + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[format('{0}_triggerManager', parameters('dataFactoryName'))]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), if(contains(parameters('tagsByResource'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), parameters('tagsByResource')['Microsoft.ManagedIdentity/userAssignedIdentities'], createObject()))]" + }, + { + "copy": { + "name": "identityRoleAssignments", + "count": "[length(variables('autoStartRbacRoles'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DataFactory/factories/{0}', parameters('dataFactoryName'))]", + "name": "[guid(resourceId('Microsoft.DataFactory/factories', parameters('dataFactoryName')), variables('autoStartRbacRoles')[copyIndex()], resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_triggerManager', parameters('dataFactoryName'))))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', variables('autoStartRbacRoles')[copyIndex()])]", + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_triggerManager', parameters('dataFactoryName'))), '2023-01-31').principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_triggerManager', parameters('dataFactoryName')))]" + ] + }, + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('{0}_stopHubTriggers', parameters('dataFactoryName'))]", + "location": "[if(startsWith(parameters('location'), 'china'), 'chinaeast2', parameters('location'))]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_triggerManager', parameters('dataFactoryName'))))]": {} + } + }, + "kind": "AzurePowerShell", + "tags": "[union(parameters('tags'), if(contains(parameters('tagsByResource'), 'Microsoft.Resources/deploymentScripts'), parameters('tagsByResource')['Microsoft.Resources/deploymentScripts'], createObject()))]", + "properties": { + "azPowerShellVersion": "8.0", + "retentionInterval": "PT1H", + "cleanupPreference": "OnSuccess", + "scriptContent": "[variables('$fxv#1')]", + "arguments": "-Stop", + "environmentVariables": [ + { + "name": "DataFactorySubscriptionId", + "value": "[subscription().id]" + }, + { + "name": "DataFactoryResourceGroup", + "value": "[resourceGroup().name]" + }, + { + "name": "DataFactoryName", + "value": "[parameters('dataFactoryName')]" + }, + { + "name": "Triggers", + "value": "[join(variables('allHubTriggers'), '|')]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_triggerManager', parameters('dataFactoryName')))]", + "identityRoleAssignments" + ] + }, + { + "type": "Microsoft.DataFactory/factories/linkedservices", + "apiVersion": "2018-06-01", + "name": "[format('{0}/{1}', parameters('dataFactoryName'), 'keyVault')]", + "properties": { + "annotations": [], + "parameters": {}, + "type": "AzureKeyVault", + "typeProperties": { + "baseUrl": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('keyVaultId'), '/')[2], split(parameters('keyVaultId'), '/')[4]), 'Microsoft.KeyVault/vaults', last(split(parameters('keyVaultId'), '/'))), '2022-11-01').vaultUri]" + } + } + }, + { + "type": "Microsoft.DataFactory/factories/linkedservices", + "apiVersion": "2018-06-01", + "name": "[format('{0}/{1}', parameters('dataFactoryName'), 'storage')]", + "properties": { + "annotations": [], + "parameters": {}, + "type": "AzureBlobFS", + "typeProperties": { + "url": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2022-09-01').primaryEndpoints.dfs]", + "accountKey": { + "type": "AzureKeyVaultSecret", + "store": { + "referenceName": "keyVault", + "type": "LinkedServiceReference" + }, + "secretName": "[parameters('storageAccountName')]" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DataFactory/factories/linkedservices', parameters('dataFactoryName'), 'keyVault')]" + ] + }, + { + "type": "Microsoft.DataFactory/factories/datasets", + "apiVersion": "2018-06-01", + "name": "[format('{0}/{1}', parameters('dataFactoryName'), variables('safeExportContainerName'))]", + "properties": { + "annotations": [], + "parameters": { + "fileName": { + "type": "String" + }, + "folderName": { + "type": "String" + } + }, + "type": "DelimitedText", + "typeProperties": "[union(variables('datasetPropsCommon'), variables('datasetPropsDelimitedText'), createObject('compressionCodec', 'none'))]", + "linkedServiceName": { + "parameters": {}, + "referenceName": "storage", + "type": "LinkedServiceReference" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DataFactory/factories/linkedservices', parameters('dataFactoryName'), 'keyVault')]", + "[resourceId('Microsoft.DataFactory/factories/linkedservices', parameters('dataFactoryName'), 'storage')]" + ] + }, + { + "type": "Microsoft.DataFactory/factories/datasets", + "apiVersion": "2018-06-01", + "name": "[format('{0}/{1}', parameters('dataFactoryName'), variables('safeIngestionContainerName'))]", + "properties": { + "annotations": [], + "parameters": { + "fileName": { + "type": "String" + }, + "folderName": { + "type": "String" + } + }, + "type": "[if(parameters('convertToParquet'), 'Parquet', 'DelimitedText')]", + "typeProperties": "[union(variables('datasetPropsCommon'), if(parameters('convertToParquet'), createObject(), variables('datasetPropsDelimitedText')), createObject('compressionCodec', 'gzip'))]", + "linkedServiceName": { + "parameters": {}, + "referenceName": "storage", + "type": "LinkedServiceReference" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DataFactory/factories/linkedservices', parameters('dataFactoryName'), 'keyVault')]", + "[resourceId('Microsoft.DataFactory/factories/linkedservices', parameters('dataFactoryName'), 'storage')]" + ] + }, + { + "type": "Microsoft.DataFactory/factories/triggers", + "apiVersion": "2018-06-01", + "name": "[format('{0}/{1}', parameters('dataFactoryName'), variables('exportFileAddedTriggerName'))]", + "properties": { + "annotations": [], + "pipelines": [ + { + "pipelineReference": { + "referenceName": "[format('{0}_ExecuteETL', parameters('exportContainerName'))]", + "type": "PipelineReference" + }, + "parameters": { + "folderName": "@triggerBody().folderPath", + "fileName": "@triggerBody().fileName" + } + } + ], + "type": "BlobEventsTrigger", + "typeProperties": { + "blobPathBeginsWith": "[format('/{0}/blobs/', parameters('exportContainerName'))]", + "blobPathEndsWith": ".csv", + "ignoreEmptyBlobs": true, + "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]", + "events": [ + "Microsoft.Storage.BlobCreated" + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DataFactory/factories/pipelines', parameters('dataFactoryName'), format('{0}_ExecuteETL', variables('safeExportContainerName')))]", + "[resourceId('Microsoft.Resources/deploymentScripts', format('{0}_stopHubTriggers', parameters('dataFactoryName')))]" + ] + }, + { + "type": "Microsoft.DataFactory/factories/pipelines", + "apiVersion": "2018-06-01", + "name": "[format('{0}/{1}', parameters('dataFactoryName'), format('{0}_ExecuteETL', variables('safeExportContainerName')))]", + "properties": { + "activities": [ + { + "name": "Execute", + "type": "ExecutePipeline", + "dependsOn": [], + "userProperties": [], + "typeProperties": { + "pipeline": { + "referenceName": "[format('{0}_ETL_{1}', variables('safeExportContainerName'), variables('safeIngestionContainerName'))]", + "type": "PipelineReference" + }, + "waitOnCompletion": false, + "parameters": { + "folderName": { + "value": "@pipeline().parameters.folderName", + "type": "Expression" + }, + "fileName": { + "value": "@pipeline().parameters.fileName", + "type": "Expression" + } + } + } + } + ], + "parameters": { + "folderName": { + "type": "string" + }, + "fileName": { + "type": "string" + } + }, + "annotations": [] + }, + "dependsOn": [ + "[resourceId('Microsoft.DataFactory/factories/pipelines', parameters('dataFactoryName'), format('{0}_ETL_{1}', variables('safeExportContainerName'), variables('safeIngestionContainerName')))]" + ] + }, + { + "type": "Microsoft.DataFactory/factories/pipelines", + "apiVersion": "2018-06-01", + "name": "[format('{0}/{1}', parameters('dataFactoryName'), format('{0}_ETL_{1}', variables('safeExportContainerName'), variables('safeIngestionContainerName')))]", + "properties": { + "activities": [ + { + "name": "Wait", + "type": "Wait", + "dependsOn": [], + "userProperties": [], + "typeProperties": { + "waitTimeInSeconds": 60 + } + }, + { + "name": "Set FolderArray", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Wait", + "dependencyConditions": [ + "Completed" + ] + } + ], + "userProperties": [], + "typeProperties": { + "variableName": "folderArray", + "value": { + "value": "@split(pipeline().parameters.folderName, '/')", + "type": "Expression" + } + } + }, + { + "name": "Set FolderCount", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Set FolderArray", + "dependencyConditions": [ + "Completed" + ] + } + ], + "policy": { + "secureOutput": false, + "secureInput": false + }, + "userProperties": [], + "typeProperties": { + "variableName": "folderCount", + "value": "@length(split(pipeline().parameters.folderName, '/'))" + } + }, + { + "name": "Set SecondToLastFolder", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Set FolderCount", + "dependencyConditions": [ + "Completed" + ] + } + ], + "policy": { + "secureOutput": false, + "secureInput": false + }, + "userProperties": [], + "typeProperties": { + "variableName": "secondToLastFolder", + "value": "@variables('folderArray')[sub(variables('folderCount'), 2)]" + } + }, + { + "name": "Set ThirdToLastFolder", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Set SecondToLastFolder", + "dependencyConditions": [ + "Succeeded" + ] + } + ], + "policy": { + "secureOutput": false, + "secureInput": false + }, + "userProperties": [], + "typeProperties": { + "variableName": "thirdToLastFolder", + "value": "@variables('folderArray')[sub(variables('folderCount'), 3)]" + } + }, + { + "name": "Set FourthToLastFolder", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Set ThirdToLastFolder", + "dependencyConditions": [ + "Succeeded" + ] + } + ], + "policy": { + "secureOutput": false, + "secureInput": false + }, + "userProperties": [], + "typeProperties": { + "variableName": "fourthToLastFolder", + "value": "@variables('folderArray')[sub(variables('folderCount'), 4)]" + } + }, + { + "name": "Set Scope", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Set FourthToLastFolder", + "dependencyConditions": [ + "Completed" + ] + } + ], + "userProperties": [], + "typeProperties": { + "variableName": "scope", + "value": { + "value": "[format('@replace(split(pipeline().parameters.folderName, if(greater(length(variables(''secondToLastFolder'')), 12), variables(''thirdToLastFolder''), variables(''fourthToLastFolder'')))[0], ''{0}'', ''{1}'')', parameters('exportContainerName'), parameters('ingestionContainerName'))]", + "type": "Expression" + } + } + }, + { + "name": "Set Metric", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Set Scope", + "dependencyConditions": [ + "Completed" + ] + } + ], + "userProperties": [], + "typeProperties": { + "variableName": "metric", + "value": { + "value": "focuscost", + "type": "Expression" + } + } + }, + { + "name": "Set Date", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Set Metric", + "dependencyConditions": [ + "Completed" + ] + } + ], + "userProperties": [], + "typeProperties": { + "variableName": "date", + "value": { + "value": "@substring(if(greater(length(variables('secondToLastFolder')), 12), variables('secondToLastFolder'), variables('thirdToLastFolder')), 0, 6)", + "type": "Expression" + } + } + }, + { + "name": "Set Destination File Name", + "description": "", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Set Date", + "dependencyConditions": [ + "Completed" + ] + } + ], + "userProperties": [], + "typeProperties": { + "variableName": "destinationFile", + "value": { + "value": "[format('@replace(pipeline().parameters.fileName, ''.csv'', ''{0}'')', if(parameters('convertToParquet'), '.parquet', '.csv.gz'))]", + "type": "Expression" + } + } + }, + { + "name": "Set Destination Folder Name", + "type": "SetVariable", + "dependsOn": [ + { + "activity": "Set Destination File Name", + "dependencyConditions": [ + "Completed" + ] + } + ], + "userProperties": [], + "typeProperties": { + "variableName": "destinationFolder", + "value": { + "value": "@replace(concat(variables('scope'),variables('date'),'/',variables('metric')),'//','/')", + "type": "Expression" + } + } + }, + { + "name": "Delete Target", + "type": "Delete", + "dependsOn": [ + { + "activity": "Set Destination Folder Name", + "dependencyConditions": [ + "Completed" + ] + } + ], + "policy": { + "timeout": "0.12:00:00", + "retry": 0, + "retryIntervalInSeconds": 30, + "secureOutput": false, + "secureInput": false + }, + "userProperties": [], + "typeProperties": { + "dataset": { + "referenceName": "[variables('safeIngestionContainerName')]", + "type": "DatasetReference", + "parameters": { + "folderName": { + "value": "@variables('destinationFolder')", + "type": "Expression" + }, + "fileName": { + "value": "@variables('destinationFile')", + "type": "Expression" + } + } + }, + "enableLogging": false, + "storeSettings": { + "type": "AzureBlobFSReadSettings", + "recursive": true, + "enablePartitionDiscovery": false + } + } + }, + { + "name": "Convert CSV", + "type": "Copy", + "dependsOn": [ + { + "activity": "Delete Target", + "dependencyConditions": [ + "Completed" + ] + } + ], + "policy": { + "timeout": "0.12:00:00", + "retry": 0, + "retryIntervalInSeconds": 30, + "secureOutput": false, + "secureInput": false + }, + "userProperties": [], + "typeProperties": { + "source": { + "type": "DelimitedTextSource", + "storeSettings": { + "type": "AzureBlobFSReadSettings", + "recursive": true, + "enablePartitionDiscovery": false + }, + "formatSettings": { + "type": "DelimitedTextReadSettings" + } + }, + "sink": { + "type": "DelimitedTextSink", + "storeSettings": { + "type": "AzureBlobFSWriteSettings" + }, + "formatSettings": "[if(parameters('convertToParquet'), createObject('type', 'ParquetWriteSettings', 'fileExtension', '.parquet'), createObject('type', 'DelimitedTextWriteSettings', 'quoteAllText', true(), 'fileExtension', '.csv.gz'))]" + }, + "enableStaging": false, + "parallelCopies": 1, + "validateDataConsistency": false, + "translator": { + "type": "TabularTranslator", + "mappings": "[variables('focusCostMappings')]" + } + }, + "inputs": [ + { + "referenceName": "[variables('safeExportContainerName')]", + "type": "DatasetReference", + "parameters": { + "folderName": { + "value": "@pipeline().parameters.folderName", + "type": "Expression" + }, + "fileName": { + "value": "@pipeline().parameters.fileName", + "type": "Expression" + } + } + } + ], + "outputs": [ + { + "referenceName": "[variables('safeIngestionContainerName')]", + "type": "DatasetReference", + "parameters": { + "folderName": { + "value": "@variables('destinationFolder')", + "type": "Expression" + }, + "fileName": { + "value": "@variables('destinationFile')", + "type": "Expression" + } + } + } + ] + }, + { + "name": "Delete CSV", + "type": "Delete", + "dependsOn": [ + { + "activity": "Convert CSV", + "dependencyConditions": [ + "Succeeded" + ] + } + ], + "policy": { + "timeout": "0.12:00:00", + "retry": 0, + "retryIntervalInSeconds": 30, + "secureOutput": false, + "secureInput": false + }, + "userProperties": [], + "typeProperties": { + "dataset": { + "referenceName": "[variables('safeExportContainerName')]", + "type": "DatasetReference", + "parameters": { + "folderName": { + "value": "@pipeline().parameters.folderName", + "type": "Expression" + }, + "fileName": { + "value": "@pipeline().parameters.fileName", + "type": "Expression" + } + } + }, + "enableLogging": false, + "storeSettings": { + "type": "AzureBlobFSReadSettings", + "recursive": true, + "enablePartitionDiscovery": false + } + } + } + ], + "parameters": { + "fileName": { + "type": "string" + }, + "folderName": { + "type": "string" + } + }, + "variables": { + "destinationFile": { + "type": "String" + }, + "destinationFolder": { + "type": "String" + }, + "folderArray": { + "type": "Array" + }, + "folderCount": { + "type": "Integer" + }, + "secondToLastFolder": { + "type": "String" + }, + "thirdToLastFolder": { + "type": "String" + }, + "fourthToLastFolder": { + "type": "String" + }, + "scope": { + "type": "String" + }, + "date": { + "type": "String" + }, + "metric": { + "type": "String" + } + }, + "annotations": [] + }, + "dependsOn": [ + "[resourceId('Microsoft.DataFactory/factories/datasets', parameters('dataFactoryName'), variables('safeIngestionContainerName'))]", + "[resourceId('Microsoft.DataFactory/factories/datasets', parameters('dataFactoryName'), variables('safeExportContainerName'))]" + ] + }, + { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2020-10-01", + "name": "[format('{0}_startHubTriggers', parameters('dataFactoryName'))]", + "location": "[if(startsWith(parameters('location'), 'china'), 'chinaeast2', parameters('location'))]", + "tags": "[union(parameters('tags'), if(contains(parameters('tagsByResource'), 'Microsoft.Resources/deploymentScripts'), parameters('tagsByResource')['Microsoft.Resources/deploymentScripts'], createObject()))]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_triggerManager', parameters('dataFactoryName'))))]": {} + } + }, + "kind": "AzurePowerShell", + "properties": { + "azPowerShellVersion": "8.0", + "retentionInterval": "PT1H", + "cleanupPreference": "OnSuccess", + "scriptContent": "[variables('$fxv#2')]", + "environmentVariables": [ + { + "name": "DataFactorySubscriptionId", + "value": "[subscription().id]" + }, + { + "name": "DataFactoryResourceGroup", + "value": "[resourceGroup().name]" + }, + { + "name": "DataFactoryName", + "value": "[parameters('dataFactoryName')]" + }, + { + "name": "Triggers", + "value": "[join(variables('allHubTriggers'), '|')]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}_triggerManager', parameters('dataFactoryName')))]", + "identityRoleAssignments", + "[resourceId('Microsoft.DataFactory/factories/triggers', parameters('dataFactoryName'), variables('exportFileAddedTriggerName'))]" + ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The Resource ID of the Data factory." + }, + "value": "[resourceId('Microsoft.DataFactory/factories', parameters('dataFactoryName'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Azure Data Factory instance." + }, + "value": "[parameters('dataFactoryName')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', 'keyVault')]", + "[resourceId('Microsoft.Resources/deployments', 'storage')]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "keyVault", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "hubName": { + "value": "[parameters('hubName')]" + }, + "existingKeyVaultName": { + "value": "[last(split(parameters('existingKeyVaultId'), '/'))]" + }, + "uniqueSuffix": { + "value": "[variables('uniqueSuffix')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[variables('resourceTags')]" + }, + "tagsByResource": { + "value": "[parameters('tagsByResource')]" + }, + "storageAccountName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value]" + }, + "accessPolicies": { + "value": [ + { + "objectId": "[reference(resourceId('Microsoft.DataFactory/factories', variables('dataFactoryName')), '2018-06-01', 'full').identity.principalId]", + "tenantId": "[subscription().tenantId]", + "permissions": { + "secrets": [ + "get" + ] + } + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "18385434849803379988" + } + }, + "parameters": { + "hubName": { + "type": "string", + "metadata": { + "description": "Required. Name of the hub. Used to ensure unique resource names." + } + }, + "uniqueSuffix": { + "type": "string", + "metadata": { + "description": "Required. Suffix to add to the KeyVault instance name to ensure uniqueness." + } + }, + "existingKeyVaultName": { + "type": "string", + "metadata": { + "description": "Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "accessPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of access policies object." + } + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Required. Name of the storage account to store access keys for." + } + }, + "sku": { + "type": "string", + "defaultValue": "premium", + "allowedValues": [ + "premium", + "standard" + ], + "metadata": { + "description": "Optional. Specifies the SKU for the vault." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "tagsByResource": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to resources based on their resource type. Resource type specific tags will be merged with tags for all resources." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedAccessPolicies", + "count": "[length(parameters('accessPolicies'))]", + "input": { + "applicationId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'applicationId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].applicationId, '')]", + "objectId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'objectId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].objectId, '')]", + "permissions": "[parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].permissions]", + "tenantId": "[if(contains(parameters('accessPolicies')[copyIndex('formattedAccessPolicies')], 'tenantId'), parameters('accessPolicies')[copyIndex('formattedAccessPolicies')].tenantId, tenant().tenantId)]" + } + } + ], + "keyVaultPrefix": "[format('{0}-vault', replace(parameters('hubName'), '_', '-'))]", + "keyVaultSuffix": "[format('-{0}', parameters('uniqueSuffix'))]", + "keyVaultName": "[replace(format('{0}{1}', take(variables('keyVaultPrefix'), sub(24, length(variables('keyVaultSuffix')))), variables('keyVaultSuffix')), '--', '-')]" + }, + "resources": [ + { + "condition": "[and(not(empty(parameters('existingKeyVaultName'))), not(empty(parameters('accessPolicies'))))]", + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName')), 'add')]", + "properties": { + "accessPolicies": "[variables('formattedAccessPolicies')]" + } + }, + { + "condition": "[not(empty(parameters('existingKeyVaultName')))]", + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName')), parameters('storageAccountName'))]", + "properties": { + "attributes": { + "enabled": true, + "exp": 1702648632, + "nbf": 10000 + }, + "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2022-09-01').keys[0].value]" + } + }, + { + "condition": "[and(empty(parameters('existingKeyVaultName')), not(empty(parameters('accessPolicies'))))]", + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', variables('keyVaultName'), 'add')]", + "properties": { + "accessPolicies": "[variables('formattedAccessPolicies')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]" + ] + }, + { + "condition": "[empty(parameters('existingKeyVaultName'))]", + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', variables('keyVaultName'), parameters('storageAccountName'))]", + "properties": { + "attributes": { + "enabled": true, + "exp": 1702648632, + "nbf": 10000 + }, + "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2022-09-01').keys[0].value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]" + ] + }, + { + "condition": "[empty(parameters('existingKeyVaultName'))]", + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[variables('keyVaultName')]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), if(contains(parameters('tagsByResource'), 'Microsoft.KeyVault/vaults'), parameters('tagsByResource')['Microsoft.KeyVault/vaults'], createObject()))]", + "properties": { + "enabledForDeployment": true, + "enabledForTemplateDeployment": true, + "enabledForDiskEncryption": true, + "enableSoftDelete": true, + "softDeleteRetentionInDays": 90, + "enableRbacAuthorization": false, + "createMode": "default", + "tenantId": "[subscription().tenantId]", + "accessPolicies": "[variables('formattedAccessPolicies')]", + "sku": { + "name": "[if(startsWith(parameters('location'), 'china'), 'standard', parameters('sku'))]", + "family": "A" + } + } + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key vault." + }, + "value": "[if(empty(parameters('existingKeyVaultName')), resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName')), resourceId('Microsoft.KeyVault/vaults', if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName'))))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the key vault." + }, + "value": "[if(empty(parameters('existingKeyVaultName')), variables('keyVaultName'), if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName')))]" + }, + "uri": { + "type": "string", + "metadata": { + "description": "The URI of the key vault." + }, + "value": "[if(empty(parameters('existingKeyVaultName')), reference(resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName')), '2023-07-01').vaultUri, reference(resourceId('Microsoft.KeyVault/vaults', if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName'))), '2023-07-01').vaultUri)]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DataFactory/factories', variables('dataFactoryName'))]", + "[resourceId('Microsoft.Resources/deployments', 'storage')]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the deployed hub instance." + }, + "value": "[parameters('hubName')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "Azure resource location resources were deployed to." + }, + "value": "[parameters('location')]" + }, + "dataFactorytName": { + "type": "string", + "metadata": { + "description": "Name of the Data Factory." + }, + "value": "[variables('dataFactoryName')]" + }, + "storageAccountId": { + "type": "string", + "metadata": { + "description": "Resource ID of the storage account created for the hub instance. This must be used when creating the Cost Management export." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.resourceId.value]" + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Name of the storage account created for the hub instance. This must be used when connecting FinOps toolkit Power BI reports to your data." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value]" + }, + "storageUrlForPowerBI": { + "type": "string", + "metadata": { + "description": "URL to use when connecting custom Power BI reports to your data." + }, + "value": "[format('https://{0}.dfs.{1}/{2}', reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value, environment().suffixes.storage, reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.ingestionContainer.value)]" + } + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the resource group." + }, + "value": "[parameters('hubName')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resources wer deployed to." + }, + "value": "[parameters('location')]" + }, + "dataFactorytName": { + "type": "string", + "metadata": { + "description": "Name of the Data Factory." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'hub'), '2022-09-01').outputs.dataFactorytName.value]" + }, + "storageAccountId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed storage account." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'hub'), '2022-09-01').outputs.storageAccountId.value]" + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Name of the storage account created for the hub instance. This must be used when connecting FinOps toolkit Power BI reports to your data." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'hub'), '2022-09-01').outputs.storageAccountName.value]" + }, + "storageUrlForPowerBI": { + "type": "string", + "metadata": { + "description": "URL to use when connecting custom Power BI reports to your data." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'hub'), '2022-09-01').outputs.storageUrlForPowerBI.value]" + } + } +} \ No newline at end of file diff --git a/docs/deploy/finops-hub-latest.json b/docs/deploy/finops-hub-latest.json index 929aef644..0e32b0cd5 100644 --- a/docs/deploy/finops-hub-latest.json +++ b/docs/deploy/finops-hub-latest.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.24.24.22086", - "templateHash": "6371350577264419703" + "templateHash": "9748975281421053488" } }, "parameters": { @@ -33,6 +33,13 @@ "description": "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." } }, + "existingKeyVaultId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created." + } + }, "tags": { "type": "object", "defaultValue": {}, @@ -75,6 +82,9 @@ "storageSku": { "value": "[parameters('storageSku')]" }, + "existingKeyVaultId": { + "value": "[parameters('existingKeyVaultId')]" + }, "tags": { "value": "[parameters('tags')]" }, @@ -92,7 +102,7 @@ "_generator": { "name": "bicep", "version": "0.24.24.22086", - "templateHash": "2610829918662778812" + "templateHash": "17411969862525330364" } }, "parameters": { @@ -120,6 +130,13 @@ "description": "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." } }, + "existingKeyVaultId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created." + } + }, "tags": { "type": "object", "defaultValue": {}, @@ -487,8 +504,8 @@ "convertToParquet": { "value": "[parameters('convertToParquet')]" }, - "keyVaultName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyVault'), '2022-09-01').outputs.name.value]" + "keyVaultId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', 'keyVault'), '2022-09-01').outputs.resourceId.value]" }, "storageAccountName": { "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.name.value]" @@ -516,20 +533,20 @@ "_generator": { "name": "bicep", "version": "0.24.24.22086", - "templateHash": "5738194981634133446" + "templateHash": "8197671316834274442" } }, "parameters": { "dataFactoryName": { "type": "string", "metadata": { - "description": "Optional. Name of the hub. Used to ensure unique resource names. Default: \"finops-hub\"." + "description": "Required. Name of the hub. Used to ensure unique resource names." } }, - "keyVaultName": { + "keyVaultId": { "type": "string", "metadata": { - "description": "Required. The name of the Azure Key Vault instance." + "description": "Optional. The resource ID of the Azure Key Vault instance." } }, "storageAccountName": { @@ -1123,7 +1140,7 @@ "parameters": {}, "type": "AzureKeyVault", "typeProperties": { - "baseUrl": "[reference(resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName')), '2022-11-01').vaultUri]" + "baseUrl": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('keyVaultId'), '/')[2], split(parameters('keyVaultId'), '/')[4]), 'Microsoft.KeyVault/vaults', last(split(parameters('keyVaultId'), '/'))), '2022-11-01').vaultUri]" } } }, @@ -1794,6 +1811,9 @@ "hubName": { "value": "[parameters('hubName')]" }, + "existingKeyVaultName": { + "value": "[last(split(parameters('existingKeyVaultId'), '/'))]" + }, "uniqueSuffix": { "value": "[variables('uniqueSuffix')]" }, @@ -1830,7 +1850,7 @@ "_generator": { "name": "bicep", "version": "0.24.24.22086", - "templateHash": "10770478197596540923" + "templateHash": "18385434849803379988" } }, "parameters": { @@ -1846,6 +1866,12 @@ "description": "Required. Suffix to add to the KeyVault instance name to ensure uniqueness." } }, + "existingKeyVaultName": { + "type": "string", + "metadata": { + "description": "Optional. Resource ID of the existing Key Vault resource to use. If not specified, a new Key Vault instance will be created." + } + }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", @@ -1911,31 +1937,32 @@ }, "resources": [ { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-11-01", - "name": "[variables('keyVaultName')]", - "location": "[parameters('location')]", - "tags": "[union(parameters('tags'), if(contains(parameters('tagsByResource'), 'Microsoft.KeyVault/vaults'), parameters('tagsByResource')['Microsoft.KeyVault/vaults'], createObject()))]", + "condition": "[and(not(empty(parameters('existingKeyVaultName'))), not(empty(parameters('accessPolicies'))))]", + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName')), 'add')]", "properties": { - "enabledForDeployment": true, - "enabledForTemplateDeployment": true, - "enabledForDiskEncryption": true, - "enableSoftDelete": true, - "softDeleteRetentionInDays": 90, - "enableRbacAuthorization": false, - "createMode": "default", - "tenantId": "[subscription().tenantId]", - "accessPolicies": "[variables('formattedAccessPolicies')]", - "sku": { - "name": "[if(startsWith(parameters('location'), 'china'), 'standard', parameters('sku'))]", - "family": "A" - } + "accessPolicies": "[variables('formattedAccessPolicies')]" } }, { - "condition": "[not(empty(parameters('accessPolicies')))]", + "condition": "[not(empty(parameters('existingKeyVaultName')))]", + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName')), parameters('storageAccountName'))]", + "properties": { + "attributes": { + "enabled": true, + "exp": 1702648632, + "nbf": 10000 + }, + "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2022-09-01').keys[0].value]" + } + }, + { + "condition": "[and(empty(parameters('existingKeyVaultName')), not(empty(parameters('accessPolicies'))))]", "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2022-11-01", + "apiVersion": "2023-07-01", "name": "[format('{0}/{1}', variables('keyVaultName'), 'add')]", "properties": { "accessPolicies": "[variables('formattedAccessPolicies')]" @@ -1945,8 +1972,9 @@ ] }, { + "condition": "[empty(parameters('existingKeyVaultName'))]", "type": "Microsoft.KeyVault/vaults/secrets", - "apiVersion": "2022-11-01", + "apiVersion": "2023-07-01", "name": "[format('{0}/{1}', variables('keyVaultName'), parameters('storageAccountName'))]", "properties": { "attributes": { @@ -1959,6 +1987,29 @@ "dependsOn": [ "[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]" ] + }, + { + "condition": "[empty(parameters('existingKeyVaultName'))]", + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[variables('keyVaultName')]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), if(contains(parameters('tagsByResource'), 'Microsoft.KeyVault/vaults'), parameters('tagsByResource')['Microsoft.KeyVault/vaults'], createObject()))]", + "properties": { + "enabledForDeployment": true, + "enabledForTemplateDeployment": true, + "enabledForDiskEncryption": true, + "enableSoftDelete": true, + "softDeleteRetentionInDays": 90, + "enableRbacAuthorization": false, + "createMode": "default", + "tenantId": "[subscription().tenantId]", + "accessPolicies": "[variables('formattedAccessPolicies')]", + "sku": { + "name": "[if(startsWith(parameters('location'), 'china'), 'standard', parameters('sku'))]", + "family": "A" + } + } } ], "outputs": { @@ -1967,21 +2018,21 @@ "metadata": { "description": "The resource ID of the key vault." }, - "value": "[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]" + "value": "[if(empty(parameters('existingKeyVaultName')), resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName')), resourceId('Microsoft.KeyVault/vaults', if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName'))))]" }, "name": { "type": "string", "metadata": { "description": "The name of the key vault." }, - "value": "[variables('keyVaultName')]" + "value": "[if(empty(parameters('existingKeyVaultName')), variables('keyVaultName'), if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName')))]" }, "uri": { "type": "string", "metadata": { "description": "The URI of the key vault." }, - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName')), '2022-11-01').vaultUri]" + "value": "[if(empty(parameters('existingKeyVaultName')), reference(resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName')), '2023-07-01').vaultUri, reference(resourceId('Microsoft.KeyVault/vaults', if(empty(parameters('existingKeyVaultName')), 'placeholder', parameters('existingKeyVaultName'))), '2023-07-01').vaultUri)]" } } } diff --git a/package-lock.json b/package-lock.json index 9bb2855d9..77390a755 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ftk", - "version": "0.2.1-rc.2", + "version": "0.2.1-rc.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ftk", - "version": "0.2.1-rc.2", + "version": "0.2.1-rc.3", "license": "MIT", "devDependencies": { "all-contributors-cli": "^6.26.1" diff --git a/package.json b/package.json index e3a395a58..54fd8d075 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ftk", - "version": "0.2.1-rc.2", + "version": "0.2.1-rc.3", "description": "Starter kits, scripts, and advanced solutions to accelerate your FinOps journey in the Microsoft Cloud.", "main": "index.js", "directories": { diff --git a/src/scripts/Package-Toolkit.ps1 b/src/scripts/Package-Toolkit.ps1 index af3ab6f05..e622bad8b 100644 --- a/src/scripts/Package-Toolkit.ps1 +++ b/src/scripts/Package-Toolkit.ps1 @@ -57,12 +57,12 @@ if ($Template -ne "*" -and -not (Test-Path $relDir)) return } -Write-Host "Packaging v$version templates..." - # Package templates $version = & "$PSScriptRoot/Get-Version" $isPrerelease = $version -like '*-*' +Write-Host "Packaging v$version templates..." + Write-Verbose "Removing existing ZIP files..." Remove-Item "$relDir/*.zip" -Force @@ -93,9 +93,9 @@ $templates = Get-ChildItem $relDir -Directory ` } # Copy azuredeploy.json to docs/deploy folder - if ($isPrerelease -and -not $Docs) + if ($Docs -or -not $isPrerelease) { - Write-Verbose "Updating $($path.Name) deployment file in docs..." + Write-Verbose "Updating $($path.Name) deployment files in docs..." Copy-Item "$path/azuredeploy.json" "$deployDir/$($path.Name)-$version.json" Copy-Item "$path/azuredeploy.json" "$deployDir/$($path.Name)-latest.json" } @@ -105,7 +105,10 @@ $templates = Get-ChildItem $relDir -Directory ` return $zip } Write-Host "✅ $($templates.Count) templates" -Write-Host "ℹ️ Deployment files updated... Please commit the changes manually..." +if ($Docs -or -not $isPrerelease) +{ + Write-Host "ℹ️ Deployment files updated... Please commit the changes manually..." +} # Copy open data files Write-Verbose "Copying open data files..."