From de87ccc70b6d3cd2f8a64947822411b44f2a67d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 May 2025 17:33:17 +0000 Subject: [PATCH 1/6] Initial plan for issue From 8c59701d59547726f4e629bb642d9baef8fd9f9f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 May 2025 17:44:07 +0000 Subject: [PATCH 2/6] Implemented custom resource naming parameters Co-authored-by: flanakin <399533+flanakin@users.noreply.github.com> --- src/templates/finops-hub/main.bicep | 12 ++++++++++++ src/templates/finops-hub/modules/hub-app.bicep | 11 ++++++++++- .../finops-hub/modules/hub-types.bicep | 17 +++++++++++++---- src/templates/finops-hub/modules/hub.bicep | 12 ++++++++++++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/templates/finops-hub/main.bicep b/src/templates/finops-hub/main.bicep index f0a6dde6d..c0d709be5 100644 --- a/src/templates/finops-hub/main.bicep +++ b/src/templates/finops-hub/main.bicep @@ -147,6 +147,15 @@ param enablePublicAccess bool = true @description('Optional. Address space for the workload. A /26 is required for the workload. Default: "10.20.30.0/26".') param virtualNetworkAddressPrefix string = '10.20.30.0/26' +@description('Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name. Must follow Azure Storage naming rules.') +param storageAccountName string = '' + +@description('Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name. Must follow Azure Data Factory naming rules.') +param dataFactoryName string = '' + +@description('Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name. Must follow Azure Key Vault naming rules.') +param keyVaultName string = '' + //============================================================================== // Resources @@ -176,6 +185,9 @@ module hub 'modules/hub.bicep' = { remoteHubStorageKey: remoteHubStorageKey enablePublicAccess: enablePublicAccess virtualNetworkAddressPrefix: virtualNetworkAddressPrefix + storageAccountName: storageAccountName + dataFactoryName: dataFactoryName + keyVaultName: keyVaultName } } diff --git a/src/templates/finops-hub/modules/hub-app.bicep b/src/templates/finops-hub/modules/hub-app.bicep index bb05e5e75..10ade8516 100644 --- a/src/templates/finops-hub/modules/hub-app.bicep +++ b/src/templates/finops-hub/modules/hub-app.bicep @@ -38,6 +38,15 @@ param features HubAppFeature[] = [] @description('Optional. Custom string with additional metadata to log. Must an alphanumeric string without spaces or special characters except for underscores and dashes. Namespace + appName + telemetryString must be 50 characters or less - additional characters will be trimmed.') param telemetryString string = '' +@description('Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name.') +param storageAccountName string = '' + +@description('Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name.') +param dataFactoryName string = '' + +@description('Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name.') +param keyVaultName string = '' + //------------------------------------------------------------------------------ // Temporary parameters that should be removed in the future //------------------------------------------------------------------------------ @@ -51,7 +60,7 @@ param coreConfig HubCoreConfig // Variables //============================================================================== -var appConfig = newAppConfig(coreConfig, publisher, namespace, appName, displayName, appVersion) +var appConfig = newAppConfig(coreConfig, publisher, namespace, appName, displayName, appVersion, storageAccountName, dataFactoryName, keyVaultName) // Features var usesDataFactory = contains(features, 'DataFactory') diff --git a/src/templates/finops-hub/modules/hub-types.bicep b/src/templates/finops-hub/modules/hub-types.bicep index 5d944b7d4..0d39fcc17 100644 --- a/src/templates/finops-hub/modules/hub-types.bicep +++ b/src/templates/finops-hub/modules/hub-types.bicep @@ -349,6 +349,9 @@ func newAppInternalConfig( appNamespace string, displayName string, version string, + customStorageName string = '', + customDataFactoryName string = '', + customKeyVaultName string = '', ) HubAppConfig => { ...coreConfig publisher: { @@ -359,13 +362,13 @@ func newAppInternalConfig( tags: union(coreConfig.hub.tags, publisherTags) // Globally unique Data Factory name: 3-63 chars; letters, numbers, non-repeating dashes - dataFactory: replace('${take('${replace(coreConfig.hub.name, '_', '-')}-engine', 63 - length(publisherSuffix))}${publisherSuffix}', '--', '-') + dataFactory: !empty(customDataFactoryName) ? customDataFactoryName : replace('${take('${replace(coreConfig.hub.name, '_', '-')}-engine', 63 - length(publisherSuffix))}${publisherSuffix}', '--', '-') // Globally unique KeyVault name: 3-24 chars; letters, numbers, dashes - keyVault: replace('${take('${replace(coreConfig.hub.name, '_', '-')}-vault', 24 - length(publisherSuffix))}${publisherSuffix}', '--', '-') + keyVault: !empty(customKeyVaultName) ? customKeyVaultName : replace('${take('${replace(coreConfig.hub.name, '_', '-')}-vault', 24 - length(publisherSuffix))}${publisherSuffix}', '--', '-') // Globally unique storage account name: 3-24 chars; lowercase letters/numbers only - storage: '${take(coreConfig.hub.safeName, 24 - length(publisherSuffix))}${publisherSuffix}' + storage: !empty(customStorageName) ? customStorageName : '${take(coreConfig.hub.safeName, 24 - length(publisherSuffix))}${publisherSuffix}' } app: { // id: '${hubResourceId}/publishers/${namespace}/apps/${appName}' @@ -389,6 +392,9 @@ func newAppConfig( appName string, displayName string, version string, + customStorageName string = '', + customDataFactoryName string = '', + customKeyVaultName string = '', ) HubAppConfig => newAppInternalConfig( config, publisher, @@ -398,7 +404,10 @@ func newAppConfig( appName, '${namespace}.${appName}', // appNamespace displayName, - version + version, + customStorageName, + customDataFactoryName, + customKeyVaultName ) @export() diff --git a/src/templates/finops-hub/modules/hub.bicep b/src/templates/finops-hub/modules/hub.bicep index 68f5e59fb..5769f8474 100644 --- a/src/templates/finops-hub/modules/hub.bicep +++ b/src/templates/finops-hub/modules/hub.bicep @@ -158,6 +158,15 @@ param enablePublicAccess bool = true @description('Optional. Address space for the workload. A /26 is required for the workload. Default: "10.20.30.0/26".') param virtualNetworkAddressPrefix string = '10.20.30.0/26' +@description('Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name.') +param storageAccountName string = '' + +@description('Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name.') +param dataFactoryName string = '' + +@description('Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name.') +param keyVaultName string = '' + @description('Optional. Enable telemetry to track anonymous module usage trends, monitor for bugs, and improve future releases.') param enableDefaultTelemetry bool = true @@ -302,6 +311,9 @@ module appRegistration 'hub-app.bicep' = { 'Storage' ] telemetryString: telemetryString + storageAccountName: storageAccountName + dataFactoryName: dataFactoryName + keyVaultName: keyVaultName coreConfig: coreConfig } From 7e5ee2c48353ebea2cc72afe6c57c53a6eff1d8b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 May 2025 17:46:30 +0000 Subject: [PATCH 3/6] Fixed function parameter and build issues Co-authored-by: flanakin <399533+flanakin@users.noreply.github.com> --- .../finops-hub/modules/hub-app.bicep | 12 +- .../finops-hub/modules/hub-types.bicep | 12 +- .../finops-hub/modules/hub-types.json | 671 ++++++++++++++++++ 3 files changed, 688 insertions(+), 7 deletions(-) create mode 100644 src/templates/finops-hub/modules/hub-types.json diff --git a/src/templates/finops-hub/modules/hub-app.bicep b/src/templates/finops-hub/modules/hub-app.bicep index 10ade8516..aff1b32a4 100644 --- a/src/templates/finops-hub/modules/hub-app.bicep +++ b/src/templates/finops-hub/modules/hub-app.bicep @@ -60,7 +60,17 @@ param coreConfig HubCoreConfig // Variables //============================================================================== -var appConfig = newAppConfig(coreConfig, publisher, namespace, appName, displayName, appVersion, storageAccountName, dataFactoryName, keyVaultName) +var appConfig = newAppConfig( + coreConfig, + publisher, + namespace, + appName, + displayName, + appVersion, + storageAccountName, + dataFactoryName, + keyVaultName +) // Features var usesDataFactory = contains(features, 'DataFactory') diff --git a/src/templates/finops-hub/modules/hub-types.bicep b/src/templates/finops-hub/modules/hub-types.bicep index 0d39fcc17..73ba0e3ad 100644 --- a/src/templates/finops-hub/modules/hub-types.bicep +++ b/src/templates/finops-hub/modules/hub-types.bicep @@ -349,9 +349,9 @@ func newAppInternalConfig( appNamespace string, displayName string, version string, - customStorageName string = '', - customDataFactoryName string = '', - customKeyVaultName string = '', + customStorageName string, + customDataFactoryName string, + customKeyVaultName string ) HubAppConfig => { ...coreConfig publisher: { @@ -392,9 +392,9 @@ func newAppConfig( appName string, displayName string, version string, - customStorageName string = '', - customDataFactoryName string = '', - customKeyVaultName string = '', + customStorageName string, + customDataFactoryName string, + customKeyVaultName string ) HubAppConfig => newAppInternalConfig( config, publisher, diff --git a/src/templates/finops-hub/modules/hub-types.json b/src/templates/finops-hub/modules/hub-types.json new file mode 100644 index 000000000..177f85c95 --- /dev/null +++ b/src/templates/finops-hub/modules/hub-types.json @@ -0,0 +1,671 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "3328278183267243822" + } + }, + "definitions": { + "IdNameObject": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "metadata": { + "id": "Fully-qualified resource ID.", + "name": "Resource name.", + "description": "Resource ID and name." + } + }, + "HubInstanceConfig": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "safeName": { + "type": "string" + }, + "suffix": { + "type": "string" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "version": { + "type": "string" + } + }, + "metadata": { + "id": "FinOps hub resource ID.", + "name": "FinOps hub instance name.", + "safeName": "Safe name of the FinOps hub instance without underscores.", + "suffix": "Unique suffix used for shared resources.", + "location": "Azure resource location of the FinOps hub instance.", + "tags": "Tags to apply to all FinOps hub resources.", + "version": "FinOps hub version number.", + "description": "FinOps hub instance details." + } + }, + "HubDeploymentConfig": { + "type": "object", + "properties": { + "tagsByResource": { + "type": "object" + }, + "storage": { + "type": "string" + }, + "isTelemetryEnabled": { + "type": "bool" + } + }, + "metadata": { + "tagsByResource": "Tags to apply to resources based on their resource type.", + "storage": "Name of the storage account used for deployment scripts.", + "isTelemetryEnabled": "Indicates whether telemetry should be enabled for deployments.", + "description": "FinOps hub deployment settings." + } + }, + "HubStorageConfig": { + "type": "object", + "properties": { + "sku": { + "type": "string" + }, + "isInfrastructureEncrypted": { + "type": "bool" + } + }, + "metadata": { + "sku": "Storage account SKU. Allowed values: \"Premium_LRS\", \"Premium_ZRS\".", + "isInfrastructureEncrypted": "Indicates whether infrastructure encryption is enabled for the storage account.", + "description": "FinOps hub storage settings to be used when creating storage accounts." + } + }, + "HubVaultConfig": { + "type": "object", + "properties": { + "sku": { + "type": "string" + } + }, + "metadata": { + "sku": "KeyVault SKU. Allowed values: \"standard\", \"premium\".", + "description": "FinOps hub KeyVault settings to be used when creating vaults." + } + }, + "HubNetworkConfig": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "isPrivate": { + "type": "bool" + }, + "addressPrefix": { + "type": "string" + }, + "dnsZones": { + "type": "object", + "properties": { + "blob": { + "$ref": "#/definitions/IdNameObject" + }, + "dfs": { + "$ref": "#/definitions/IdNameObject" + }, + "queue": { + "$ref": "#/definitions/IdNameObject" + }, + "table": { + "$ref": "#/definitions/IdNameObject" + } + } + }, + "subnets": { + "type": "object", + "properties": { + "dataFactory": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "scripts": { + "type": "string" + }, + "storage": { + "type": "string" + } + } + } + }, + "metadata": { + "id": "Resource ID of the FinOps hub isolated virtual network, if private networking is enabled.", + "name": "Name of the FinOps hub isolated virtual network, if private networking is enabled.", + "isPrivate": "Indicates whether private networking is enabled.", + "addressPrefix": "Address prefix for the FinOps hub isolated virtual network, if private networking is enabled.", + "dnsZones": { + "blob": "Resource ID and name for the blob storage DNS zone.", + "dfs": "Resource ID and name for the DFS storage DNS zone.", + "queue": "Resource ID and name for the queue storage DNS zone.", + "table": "Resource ID and name for the table storage DNS zone." + }, + "subnets": { + "dataFactory": "Resource ID of the subnet for Data Factory instances.", + "keyVault": "Resource ID of the subnet for Key Vault instances.", + "scripts": "Resource ID of the subnet for deployment script storage.", + "storage": "Resource ID of the subnet for storage accounts." + }, + "description": "FinOps hub network settings." + } + }, + "HubCoreConfig": { + "type": "object", + "properties": { + "hub": { + "$ref": "#/definitions/HubInstanceConfig" + }, + "deployment": { + "$ref": "#/definitions/HubDeploymentConfig" + }, + "storage": { + "$ref": "#/definitions/HubStorageConfig" + }, + "keyVault": { + "$ref": "#/definitions/HubVaultConfig" + }, + "network": { + "$ref": "#/definitions/HubNetworkConfig" + } + }, + "metadata": { + "__bicep_export!": true, + "hub": "FinOps hub instance details", + "deployment": "FinOps hub deployment details", + "storage": "FinOps hub storage details", + "keyVault": "FinOps hub KeyVault details", + "network": "FinOps hub network details", + "description": "FinOps hub configuration settings." + } + }, + "HubAppConfig": { + "type": "object", + "properties": { + "hub": { + "$ref": "#/definitions/HubInstanceConfig" + }, + "deployment": { + "$ref": "#/definitions/HubDeploymentConfig" + }, + "storage": { + "$ref": "#/definitions/HubStorageConfig" + }, + "keyVault": { + "$ref": "#/definitions/HubVaultConfig" + }, + "network": { + "$ref": "#/definitions/HubNetworkConfig" + }, + "publisher": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "suffix": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "dataFactory": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "storage": { + "type": "string" + } + } + }, + "app": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "fullName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "tags": { + "type": "object" + } + } + } + }, + "metadata": { + "__bicep_export!": true, + "hub": "FinOps hub instance details", + "deployment": "FinOps hub deployment details", + "storage": "FinOps hub storage details", + "network": "FinOps hub network details", + "publisher": { + "uniqueId": "Unique suffix used for publisher resources.", + "name": "Fully-qualified namespace of the FinOps hub app publisher.", + "displayName": "Display name of the FinOps hub app publisher.", + "tags": "Tags to apply to all FinOps hub resources for this FinOps hub app publisher.", + "dataFactory": "Name of the Data Factory instance for this publisher.", + "keyVault": "Name of the KeyVault instance for this publisher.", + "storage": "Name of the storage account for this publisher.", + "subnetId": "Resource ID of the private endpoint subnet for the storage account." + }, + "app": { + "name": "Short name of the FinOps hub app (not including the publisher namespace).", + "fullName": "Fully-qualified namespace of the FinOps hub app.", + "displayName": "Display name of the FinOps hub app.", + "tags": "Tags to apply to all FinOps hub resources for this FinOps hub app." + }, + "description": "FinOps hub app configuration settings." + } + }, + "HubAppFeature": { + "type": "string", + "allowedValues": [ + "DataFactory", + "KeyVault", + "Storage" + ], + "metadata": { + "__bicep_export!": true, + "description": "FinOps hub app features." + } + } + }, + "functions": [ + { + "namespace": "__bicep", + "members": { + "safeName": { + "parameters": [ + { + "type": "string", + "name": "name" + } + ], + "output": { + "type": "string", + "value": "[replace(replace(toLower(parameters('name')), '-', ''), '_', '')]" + } + }, + "idName": { + "parameters": [ + { + "type": "string", + "name": "name" + }, + { + "type": "string", + "name": "resourceType" + } + ], + "output": { + "$ref": "#/definitions/IdNameObject", + "value": { + "id": "[resourceId(parameters('resourceType'), parameters('name'))]", + "name": "[parameters('name')]" + } + } + }, + "dnsZoneIdName": { + "parameters": [ + { + "type": "string", + "name": "type" + } + ], + "output": { + "$ref": "#/definitions/IdNameObject", + "value": "[__bicep.idName(format('privatelink.{0}.{1}', parameters('type'), environment().suffixes.storage), 'Microsoft.Network/privateDnsZones')]" + } + }, + "newHubCoreConfigInternal": { + "parameters": [ + { + "type": "string", + "name": "id" + }, + { + "type": "string", + "name": "name" + }, + { + "type": "string", + "name": "safeName" + }, + { + "type": "string", + "name": "suffix" + }, + { + "type": "string", + "name": "location" + }, + { + "type": "object", + "name": "tags" + }, + { + "type": "object", + "name": "tagsByResource" + }, + { + "type": "string", + "name": "storageSku" + }, + { + "type": "string", + "name": "keyVaultSku" + }, + { + "type": "bool", + "name": "enableInfrastructureEncryption" + }, + { + "type": "bool", + "name": "enablePublicAccess" + }, + { + "type": "string", + "name": "networkName" + }, + { + "type": "string", + "name": "networkAddressPrefix" + }, + { + "type": "bool", + "name": "isTelemetryEnabled" + } + ], + "output": { + "$ref": "#/definitions/HubCoreConfig", + "value": { + "hub": { + "id": "[parameters('id')]", + "name": "[parameters('name')]", + "safeName": "[parameters('safeName')]", + "suffix": "[parameters('suffix')]", + "location": "[coalesce(parameters('location'), resourceGroup().location)]", + "tags": "[union(parameters('tags'), createObject('cm-resource-parent', parameters('id'), 'ftk-tool', 'FinOps hubs', 'ftk-version', variables('finOpsToolkitVersion')))]", + "version": "[variables('finOpsToolkitVersion')]" + }, + "deployment": "[union(createObject('tagsByResource', parameters('tagsByResource'), 'isTelemetryEnabled', coalesce(parameters('isTelemetryEnabled'), true())), if(parameters('enablePublicAccess'), createObject('storage', ''), createObject('storage', format('{0}script{1}', take(parameters('safeName'), sub(16, length(parameters('suffix')))), parameters('suffix')))))]", + "storage": { + "sku": "[parameters('storageSku')]", + "isInfrastructureEncrypted": "[parameters('enableInfrastructureEncryption')]" + }, + "keyVault": { + "sku": "[parameters('keyVaultSku')]" + }, + "network": "[if(parameters('enablePublicAccess'), createObject('isPrivate', false(), 'id', '', 'name', '', 'addressPrefix', '', 'dnsZones', createObject('blob', createObject('id', '', 'name', ''), 'dfs', createObject('id', '', 'name', ''), 'queue', createObject('id', '', 'name', ''), 'table', createObject('id', '', 'name', '')), 'subnets', createObject('dataFactory', '', 'keyVault', '', 'scripts', '', 'storage', '')), createObject('isPrivate', true(), 'id', resourceId('Microsoft.Network/virtualNetworks', parameters('networkName')), 'name', parameters('networkName'), 'addressPrefix', parameters('networkAddressPrefix'), 'dnsZones', createObject('blob', __bicep.dnsZoneIdName('blob'), 'dfs', __bicep.dnsZoneIdName('dfs'), 'queue', __bicep.dnsZoneIdName('queue'), 'table', __bicep.dnsZoneIdName('table')), 'subnets', createObject('dataFactory', resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), 'private-endpoint-subnet'), 'keyVault', resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), 'private-endpoint-subnet'), 'scripts', resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), 'script-subnet'), 'storage', resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), 'private-endpoint-subnet'))))]" + } + } + }, + "newHubCoreConfig": { + "parameters": [ + { + "type": "string", + "name": "name" + }, + { + "type": "string", + "name": "location" + }, + { + "type": "object", + "name": "tags" + }, + { + "type": "object", + "name": "tagsByResource" + }, + { + "type": "string", + "name": "storageSku" + }, + { + "type": "string", + "name": "keyVaultSku" + }, + { + "type": "bool", + "name": "enableInfrastructureEncryption" + }, + { + "type": "bool", + "name": "enablePublicAccess" + }, + { + "type": "string", + "name": "networkAddressPrefix" + }, + { + "type": "bool", + "name": "isTelemetryEnabled" + } + ], + "output": { + "$ref": "#/definitions/HubCoreConfig", + "value": "[__bicep.newHubCoreConfigInternal(format('{0}/providers/Microsoft.Cloud/hubs/{1}', resourceGroup().id, parameters('name')), parameters('name'), __bicep.safeName(parameters('name')), uniqueString(parameters('name'), resourceGroup().id), parameters('location'), parameters('tags'), parameters('tagsByResource'), parameters('storageSku'), parameters('keyVaultSku'), parameters('enableInfrastructureEncryption'), parameters('enablePublicAccess'), format('{0}-vnet-{1}', __bicep.safeName(parameters('name')), parameters('location')), parameters('networkAddressPrefix'), coalesce(parameters('isTelemetryEnabled'), true()))]" + }, + "metadata": { + "description": "Creates a new FinOps hub configuration object.", + "__bicep_export!": true + } + }, + "newAppInternalConfig": { + "parameters": [ + { + "$ref": "#/definitions/HubCoreConfig", + "name": "coreConfig" + }, + { + "type": "string", + "name": "publisher" + }, + { + "type": "string", + "name": "namespace" + }, + { + "type": "string", + "name": "publisherSuffix" + }, + { + "type": "object", + "name": "publisherTags" + }, + { + "type": "string", + "name": "appName" + }, + { + "type": "string", + "name": "appNamespace" + }, + { + "type": "string", + "name": "displayName" + }, + { + "type": "string", + "name": "version" + }, + { + "type": "string", + "name": "customStorageName" + }, + { + "type": "string", + "name": "customDataFactoryName" + }, + { + "type": "string", + "name": "customKeyVaultName" + } + ], + "output": { + "$ref": "#/definitions/HubAppConfig", + "value": "[shallowMerge(createArray(parameters('coreConfig'), createObject('publisher', createObject('name', parameters('namespace'), 'displayName', parameters('publisher'), 'suffix', parameters('publisherSuffix'), 'tags', union(parameters('coreConfig').hub.tags, parameters('publisherTags')), 'dataFactory', if(not(empty(parameters('customDataFactoryName'))), parameters('customDataFactoryName'), replace(format('{0}{1}', take(format('{0}-engine', replace(parameters('coreConfig').hub.name, '_', '-')), sub(63, length(parameters('publisherSuffix')))), parameters('publisherSuffix')), '--', '-')), 'keyVault', if(not(empty(parameters('customKeyVaultName'))), parameters('customKeyVaultName'), replace(format('{0}{1}', take(format('{0}-vault', replace(parameters('coreConfig').hub.name, '_', '-')), sub(24, length(parameters('publisherSuffix')))), parameters('publisherSuffix')), '--', '-')), 'storage', if(not(empty(parameters('customStorageName'))), parameters('customStorageName'), format('{0}{1}', take(parameters('coreConfig').hub.safeName, sub(24, length(parameters('publisherSuffix')))), parameters('publisherSuffix')))), 'app', createObject('name', parameters('appName'), 'fullName', parameters('appNamespace'), 'displayName', parameters('displayName'), 'tags', union(parameters('coreConfig').hub.tags, parameters('publisherTags'), createObject('ftk-hubapp', parameters('appNamespace'), 'ftk-hubapp-version', parameters('version')))))))]" + } + }, + "newAppConfig": { + "parameters": [ + { + "$ref": "#/definitions/HubCoreConfig", + "name": "config" + }, + { + "type": "string", + "name": "publisher" + }, + { + "type": "string", + "name": "namespace" + }, + { + "type": "string", + "name": "appName" + }, + { + "type": "string", + "name": "displayName" + }, + { + "type": "string", + "name": "version" + }, + { + "type": "string", + "name": "customStorageName" + }, + { + "type": "string", + "name": "customDataFactoryName" + }, + { + "type": "string", + "name": "customKeyVaultName" + } + ], + "output": { + "$ref": "#/definitions/HubAppConfig", + "value": "[__bicep.newAppInternalConfig(parameters('config'), parameters('publisher'), parameters('namespace'), if(or(not(variables('usePublisherSpecificResources')), equals(parameters('namespace'), 'Microsoft.FinOpsToolkit.Hubs')), parameters('config').hub.suffix, uniqueString(parameters('namespace'))), createObject('ftk-hubapp-publisher', parameters('namespace')), parameters('appName'), format('{0}.{1}', parameters('namespace'), parameters('appName')), parameters('displayName'), parameters('version'), parameters('customStorageName'), parameters('customDataFactoryName'), parameters('customKeyVaultName'))]" + }, + "metadata": { + "description": "Creates a new FinOps hub app configuration object.", + "__bicep_export!": true + } + }, + "getHubTags": { + "parameters": [ + { + "$ref": "#/definitions/HubCoreConfig", + "name": "config" + }, + { + "type": "string", + "name": "resourceType" + } + ], + "output": { + "type": "object", + "value": "[union(parameters('config').hub.tags, coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" + }, + "metadata": { + "description": "Returns a tags dictionary that includes tags for the FinOps hub instance.", + "__bicep_export!": true + } + }, + "getPublisherTags": { + "parameters": [ + { + "$ref": "#/definitions/HubAppConfig", + "name": "config" + }, + { + "type": "string", + "name": "resourceType" + } + ], + "output": { + "type": "object", + "value": "[union(if(variables('usePublisherSpecificResources'), parameters('config').publisher.tags, parameters('config').hub.tags), coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" + }, + "metadata": { + "description": "Returns a tags dictionary that includes tags for the FinOps hub app publisher.", + "__bicep_export!": true + } + }, + "getAppTags": { + "parameters": [ + { + "$ref": "#/definitions/HubAppConfig", + "name": "config" + }, + { + "type": "string", + "name": "resourceType" + }, + { + "type": "bool", + "nullable": true, + "name": "forceAppTags" + } + ], + "output": { + "type": "object", + "value": "[union(if(or(variables('usePublisherSpecificResources'), coalesce(parameters('forceAppTags'), false())), parameters('config').app.tags, parameters('config').hub.tags), coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" + }, + "metadata": { + "description": "Returns a tags dictionary that includes tags for the FinOps hub app.", + "__bicep_export!": true + } + } + } + } + ], + "variables": { + "usePublisherSpecificResources": false, + "finOpsToolkitVersion": "0.11-dev" + }, + "resources": {} +} \ No newline at end of file From 2a912e8c11bd0860bcd126109ec4b4af340c44b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 May 2025 17:49:22 +0000 Subject: [PATCH 4/6] Final documentation updates Co-authored-by: flanakin <399533+flanakin@users.noreply.github.com> --- src/templates/finops-hub/modules/hub-app.json | 905 ++++++++++++++++++ 1 file changed, 905 insertions(+) create mode 100644 src/templates/finops-hub/modules/hub-app.json diff --git a/src/templates/finops-hub/modules/hub-app.json b/src/templates/finops-hub/modules/hub-app.json new file mode 100644 index 000000000..d47a10282 --- /dev/null +++ b/src/templates/finops-hub/modules/hub-app.json @@ -0,0 +1,905 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "14670356862329858753" + } + }, + "definitions": { + "_1.HubDeploymentConfig": { + "type": "object", + "properties": { + "tagsByResource": { + "type": "object" + }, + "storage": { + "type": "string" + }, + "isTelemetryEnabled": { + "type": "bool" + } + }, + "metadata": { + "tagsByResource": "Tags to apply to resources based on their resource type.", + "storage": "Name of the storage account used for deployment scripts.", + "isTelemetryEnabled": "Indicates whether telemetry should be enabled for deployments.", + "description": "FinOps hub deployment settings.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "_1.HubInstanceConfig": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "safeName": { + "type": "string" + }, + "suffix": { + "type": "string" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "version": { + "type": "string" + } + }, + "metadata": { + "id": "FinOps hub resource ID.", + "name": "FinOps hub instance name.", + "safeName": "Safe name of the FinOps hub instance without underscores.", + "suffix": "Unique suffix used for shared resources.", + "location": "Azure resource location of the FinOps hub instance.", + "tags": "Tags to apply to all FinOps hub resources.", + "version": "FinOps hub version number.", + "description": "FinOps hub instance details.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "_1.HubNetworkConfig": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "isPrivate": { + "type": "bool" + }, + "addressPrefix": { + "type": "string" + }, + "dnsZones": { + "type": "object", + "properties": { + "blob": { + "$ref": "#/definitions/_1.IdNameObject" + }, + "dfs": { + "$ref": "#/definitions/_1.IdNameObject" + }, + "queue": { + "$ref": "#/definitions/_1.IdNameObject" + }, + "table": { + "$ref": "#/definitions/_1.IdNameObject" + } + } + }, + "subnets": { + "type": "object", + "properties": { + "dataFactory": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "scripts": { + "type": "string" + }, + "storage": { + "type": "string" + } + } + } + }, + "metadata": { + "id": "Resource ID of the FinOps hub isolated virtual network, if private networking is enabled.", + "name": "Name of the FinOps hub isolated virtual network, if private networking is enabled.", + "isPrivate": "Indicates whether private networking is enabled.", + "addressPrefix": "Address prefix for the FinOps hub isolated virtual network, if private networking is enabled.", + "dnsZones": { + "blob": "Resource ID and name for the blob storage DNS zone.", + "dfs": "Resource ID and name for the DFS storage DNS zone.", + "queue": "Resource ID and name for the queue storage DNS zone.", + "table": "Resource ID and name for the table storage DNS zone." + }, + "subnets": { + "dataFactory": "Resource ID of the subnet for Data Factory instances.", + "keyVault": "Resource ID of the subnet for Key Vault instances.", + "scripts": "Resource ID of the subnet for deployment script storage.", + "storage": "Resource ID of the subnet for storage accounts." + }, + "description": "FinOps hub network settings.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "_1.HubStorageConfig": { + "type": "object", + "properties": { + "sku": { + "type": "string" + }, + "isInfrastructureEncrypted": { + "type": "bool" + } + }, + "metadata": { + "sku": "Storage account SKU. Allowed values: \"Premium_LRS\", \"Premium_ZRS\".", + "isInfrastructureEncrypted": "Indicates whether infrastructure encryption is enabled for the storage account.", + "description": "FinOps hub storage settings to be used when creating storage accounts.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "_1.HubVaultConfig": { + "type": "object", + "properties": { + "sku": { + "type": "string" + } + }, + "metadata": { + "sku": "KeyVault SKU. Allowed values: \"standard\", \"premium\".", + "description": "FinOps hub KeyVault settings to be used when creating vaults.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "_1.IdNameObject": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "metadata": { + "id": "Fully-qualified resource ID.", + "name": "Resource name.", + "description": "Resource ID and name.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "HubAppConfig": { + "type": "object", + "properties": { + "hub": { + "$ref": "#/definitions/_1.HubInstanceConfig" + }, + "deployment": { + "$ref": "#/definitions/_1.HubDeploymentConfig" + }, + "storage": { + "$ref": "#/definitions/_1.HubStorageConfig" + }, + "keyVault": { + "$ref": "#/definitions/_1.HubVaultConfig" + }, + "network": { + "$ref": "#/definitions/_1.HubNetworkConfig" + }, + "publisher": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "suffix": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "dataFactory": { + "type": "string" + }, + "keyVault": { + "type": "string" + }, + "storage": { + "type": "string" + } + } + }, + "app": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "fullName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "tags": { + "type": "object" + } + } + } + }, + "metadata": { + "hub": "FinOps hub instance details", + "deployment": "FinOps hub deployment details", + "storage": "FinOps hub storage details", + "network": "FinOps hub network details", + "publisher": { + "uniqueId": "Unique suffix used for publisher resources.", + "name": "Fully-qualified namespace of the FinOps hub app publisher.", + "displayName": "Display name of the FinOps hub app publisher.", + "tags": "Tags to apply to all FinOps hub resources for this FinOps hub app publisher.", + "dataFactory": "Name of the Data Factory instance for this publisher.", + "keyVault": "Name of the KeyVault instance for this publisher.", + "storage": "Name of the storage account for this publisher.", + "subnetId": "Resource ID of the private endpoint subnet for the storage account." + }, + "app": { + "name": "Short name of the FinOps hub app (not including the publisher namespace).", + "fullName": "Fully-qualified namespace of the FinOps hub app.", + "displayName": "Display name of the FinOps hub app.", + "tags": "Tags to apply to all FinOps hub resources for this FinOps hub app." + }, + "description": "FinOps hub app configuration settings.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "HubAppFeature": { + "type": "string", + "allowedValues": [ + "DataFactory", + "KeyVault", + "Storage" + ], + "metadata": { + "description": "FinOps hub app features.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "HubCoreConfig": { + "type": "object", + "properties": { + "hub": { + "$ref": "#/definitions/_1.HubInstanceConfig" + }, + "deployment": { + "$ref": "#/definitions/_1.HubDeploymentConfig" + }, + "storage": { + "$ref": "#/definitions/_1.HubStorageConfig" + }, + "keyVault": { + "$ref": "#/definitions/_1.HubVaultConfig" + }, + "network": { + "$ref": "#/definitions/_1.HubNetworkConfig" + } + }, + "metadata": { + "hub": "FinOps hub instance details", + "deployment": "FinOps hub deployment details", + "storage": "FinOps hub storage details", + "keyVault": "FinOps hub KeyVault details", + "network": "FinOps hub network details", + "description": "FinOps hub configuration settings.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + } + }, + "functions": [ + { + "namespace": "__bicep", + "members": { + "getAppTags": { + "parameters": [ + { + "$ref": "#/definitions/HubAppConfig", + "name": "config" + }, + { + "type": "string", + "name": "resourceType" + }, + { + "type": "bool", + "nullable": true, + "name": "forceAppTags" + } + ], + "output": { + "type": "object", + "value": "[union(if(or(variables('_1.usePublisherSpecificResources'), coalesce(parameters('forceAppTags'), false())), parameters('config').app.tags, parameters('config').hub.tags), coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" + }, + "metadata": { + "description": "Returns a tags dictionary that includes tags for the FinOps hub app.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "getPublisherTags": { + "parameters": [ + { + "$ref": "#/definitions/HubAppConfig", + "name": "config" + }, + { + "type": "string", + "name": "resourceType" + } + ], + "output": { + "type": "object", + "value": "[union(if(variables('_1.usePublisherSpecificResources'), parameters('config').publisher.tags, parameters('config').hub.tags), coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" + }, + "metadata": { + "description": "Returns a tags dictionary that includes tags for the FinOps hub app publisher.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + }, + "newAppConfig": { + "parameters": [ + { + "$ref": "#/definitions/HubCoreConfig", + "name": "config" + }, + { + "type": "string", + "name": "publisher" + }, + { + "type": "string", + "name": "namespace" + }, + { + "type": "string", + "name": "appName" + }, + { + "type": "string", + "name": "displayName" + }, + { + "type": "string", + "name": "version" + }, + { + "type": "string", + "name": "customStorageName" + }, + { + "type": "string", + "name": "customDataFactoryName" + }, + { + "type": "string", + "name": "customKeyVaultName" + } + ], + "output": { + "$ref": "#/definitions/HubAppConfig", + "value": "[_1.newAppInternalConfig(parameters('config'), parameters('publisher'), parameters('namespace'), if(or(not(variables('_1.usePublisherSpecificResources')), equals(parameters('namespace'), 'Microsoft.FinOpsToolkit.Hubs')), parameters('config').hub.suffix, uniqueString(parameters('namespace'))), createObject('ftk-hubapp-publisher', parameters('namespace')), parameters('appName'), format('{0}.{1}', parameters('namespace'), parameters('appName')), parameters('displayName'), parameters('version'), parameters('customStorageName'), parameters('customDataFactoryName'), parameters('customKeyVaultName'))]" + }, + "metadata": { + "description": "Creates a new FinOps hub app configuration object.", + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + } + } + }, + { + "namespace": "_1", + "members": { + "newAppInternalConfig": { + "parameters": [ + { + "$ref": "#/definitions/HubCoreConfig", + "name": "coreConfig" + }, + { + "type": "string", + "name": "publisher" + }, + { + "type": "string", + "name": "namespace" + }, + { + "type": "string", + "name": "publisherSuffix" + }, + { + "type": "object", + "name": "publisherTags" + }, + { + "type": "string", + "name": "appName" + }, + { + "type": "string", + "name": "appNamespace" + }, + { + "type": "string", + "name": "displayName" + }, + { + "type": "string", + "name": "version" + }, + { + "type": "string", + "name": "customStorageName" + }, + { + "type": "string", + "name": "customDataFactoryName" + }, + { + "type": "string", + "name": "customKeyVaultName" + } + ], + "output": { + "$ref": "#/definitions/HubAppConfig", + "value": "[shallowMerge(createArray(parameters('coreConfig'), createObject('publisher', createObject('name', parameters('namespace'), 'displayName', parameters('publisher'), 'suffix', parameters('publisherSuffix'), 'tags', union(parameters('coreConfig').hub.tags, parameters('publisherTags')), 'dataFactory', if(not(empty(parameters('customDataFactoryName'))), parameters('customDataFactoryName'), replace(format('{0}{1}', take(format('{0}-engine', replace(parameters('coreConfig').hub.name, '_', '-')), sub(63, length(parameters('publisherSuffix')))), parameters('publisherSuffix')), '--', '-')), 'keyVault', if(not(empty(parameters('customKeyVaultName'))), parameters('customKeyVaultName'), replace(format('{0}{1}', take(format('{0}-vault', replace(parameters('coreConfig').hub.name, '_', '-')), sub(24, length(parameters('publisherSuffix')))), parameters('publisherSuffix')), '--', '-')), 'storage', if(not(empty(parameters('customStorageName'))), parameters('customStorageName'), format('{0}{1}', take(parameters('coreConfig').hub.safeName, sub(24, length(parameters('publisherSuffix')))), parameters('publisherSuffix')))), 'app', createObject('name', parameters('appName'), 'fullName', parameters('appNamespace'), 'displayName', parameters('displayName'), 'tags', union(parameters('coreConfig').hub.tags, parameters('publisherTags'), createObject('ftk-hubapp', parameters('appNamespace'), 'ftk-hubapp-version', parameters('version')))))))]" + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "hub-types.bicep" + } + } + } + } + } + ], + "parameters": { + "publisher": { + "type": "string", + "metadata": { + "description": "Required. Display name of the FinOps hub app publisher." + } + }, + "namespace": { + "type": "string", + "metadata": { + "description": "Required. Namespace to use for the FinOps hub app publisher. Will be combined with appName to form a fully-qualified identifier. Must be an alphanumeric string without spaces or special characters except for periods. This value should never change and will be used to uniquely identify the publisher. A change would require migrating content to the new publisher. Namespace + appName + telemetryString must be 50 characters or less - additional characters will be trimmed." + } + }, + "appName": { + "type": "string", + "metadata": { + "description": "Required. Unique identifier of the FinOps hub app within the publisher namespace. Must be an alphanumeric string without spaces or special characters. This name should never change and will be used with the namespace to fully qualify the app. A change would require migrating content to the new app. Namespace + appName + telemetryString must be 50 characters or less - additional characters will be trimmed." + } + }, + "displayName": { + "type": "string", + "metadata": { + "description": "Required. Display name of the FinOps hub app." + } + }, + "appVersion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Version number of the FinOps hub app." + } + }, + "features": { + "type": "array", + "items": { + "$ref": "#/definitions/HubAppFeature" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. Indicate which features the app requires. Allowed values: \"Storage\". Default: [] (none)." + } + }, + "telemetryString": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Custom string with additional metadata to log. Must an alphanumeric string without spaces or special characters except for underscores and dashes. Namespace + appName + telemetryString must be 50 characters or less - additional characters will be trimmed." + } + }, + "storageAccountName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name." + } + }, + "dataFactoryName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name." + } + }, + "keyVaultName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name." + } + }, + "coreConfig": { + "$ref": "#/definitions/HubCoreConfig", + "metadata": { + "description": "Required. FinOps hub coreConfig." + } + } + }, + "variables": { + "appConfig": "[__bicep.newAppConfig(parameters('coreConfig'), parameters('publisher'), parameters('namespace'), parameters('appName'), parameters('displayName'), parameters('appVersion'), parameters('storageAccountName'), parameters('dataFactoryName'), parameters('keyVaultName'))]", + "usesDataFactory": "[contains(parameters('features'), 'DataFactory')]", + "usesKeyVault": "[contains(parameters('features'), 'KeyVault')]", + "usesStorage": "[contains(parameters('features'), 'Storage')]", + "telemetryId": "[format('ftk-hubapp-{0}{1}{2}', variables('appConfig').app.fullName, if(empty(parameters('telemetryString')), '', '_'), parameters('telemetryString'))]", + "telemetryProps": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "[format('FTK: {0} - {1} {2}', parameters('publisher'), parameters('displayName'), variables('telemetryId'))]", + "version": "[parameters('appVersion')]" + } + }, + "resources": [] + } + }, + "storageInfrastructureEncryptionProperties": "[if(not(parameters('coreConfig').storage.isInfrastructureEncrypted), createObject(), createObject('encryption', createObject('keySource', 'Microsoft.Storage', 'requireInfrastructureEncryption', parameters('coreConfig').storage.isInfrastructureEncrypted)))]", + "_1.usePublisherSpecificResources": false + }, + "resources": { + "blobEndpoint::blobPrivateDnsZoneGroup": { + "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', format('{0}-blob-ep', variables('appConfig').publisher.storage), 'storage-endpoint-zone')]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "[format('privatelink.blob.{0}', environment().suffixes.storage)]", + "properties": { + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', format('privatelink.blob.{0}', environment().suffixes.storage))]" + } + } + ] + }, + "dependsOn": [ + "blobEndpoint" + ] + }, + "dfsEndpoint::dfsPrivateDnsZoneGroup": { + "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', format('{0}-dfs-ep', variables('appConfig').publisher.storage), 'dfs-endpoint-zone')]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "[format('privatelink.dfs.{0}', environment().suffixes.storage)]", + "properties": { + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', format('privatelink.dfs.{0}', environment().suffixes.storage))]" + } + } + ] + }, + "dependsOn": [ + "dfsEndpoint" + ] + }, + "keyVault::keyVault_accessPolicies": { + "condition": "[variables('usesKeyVault')]", + "type": "Microsoft.KeyVault/vaults/accessPolicies", + "apiVersion": "2023-02-01", + "name": "[format('{0}/{1}', variables('appConfig').publisher.keyVault, 'add')]", + "properties": { + "accessPolicies": [ + { + "objectId": "[reference('dataFactory', '2018-06-01', 'full').identity.principalId]", + "tenantId": "[subscription().tenantId]", + "permissions": { + "secrets": [ + "get" + ] + } + } + ] + }, + "dependsOn": [ + "dataFactory", + "keyVault" + ] + }, + "keyVaultPrivateDnsZone::keyVaultPrivateDnsZoneLink": { + "condition": "[and(variables('usesKeyVault'), parameters('coreConfig').network.isPrivate)]", + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", + "name": "[format('{0}/{1}', format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore')), format('{0}-link', replace(format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore')), '.', '-')))]", + "location": "global", + "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateDnsZones/virtualNetworkLinks')]", + "properties": { + "virtualNetwork": { + "id": "[parameters('coreConfig').network.id]" + }, + "registrationEnabled": false + }, + "dependsOn": [ + "keyVaultPrivateDnsZone" + ] + }, + "keyVaultEndpoint::keyVaultPrivateDnsZoneGroup": { + "condition": "[and(variables('usesKeyVault'), parameters('coreConfig').network.isPrivate)]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', format('{0}-ep', variables('appConfig').publisher.keyVault), 'keyvault-endpoint-zone')]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "[format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore'))]", + "properties": { + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore')))]" + } + } + ] + }, + "dependsOn": [ + "keyVaultEndpoint", + "keyVaultPrivateDnsZone" + ] + }, + "appTelemetry": { + "condition": "[parameters('coreConfig').deployment.isTelemetryEnabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[if(lessOrEquals(length(variables('telemetryId')), 64), variables('telemetryId'), substring(variables('telemetryId'), 0, 64))]", + "tags": "[__bicep.getAppTags(variables('appConfig'), 'Microsoft.Resources/deployments', true())]", + "properties": "[variables('telemetryProps')]" + }, + "dataFactory": { + "condition": "[variables('usesDataFactory')]", + "type": "Microsoft.DataFactory/factories", + "apiVersion": "2018-06-01", + "name": "[variables('appConfig').publisher.dataFactory]", + "location": "[variables('appConfig').hub.location]", + "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.DataFactory/factories')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "globalConfigurations": { + "PipelineBillingEnabled": "true" + } + } + }, + "storageAccount": { + "condition": "[variables('usesStorage')]", + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[variables('appConfig').publisher.storage]", + "location": "[parameters('coreConfig').hub.location]", + "sku": { + "name": "[parameters('coreConfig').storage.sku]" + }, + "kind": "BlockBlobStorage", + "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Storage/storageAccounts')]", + "properties": "[shallowMerge(createArray(variables('storageInfrastructureEncryptionProperties'), createObject('supportsHttpsTrafficOnly', true(), 'allowSharedKeyAccess', true(), 'isHnsEnabled', true(), 'minimumTlsVersion', 'TLS1_2', 'allowBlobPublicAccess', false(), 'publicNetworkAccess', 'Enabled', 'networkAcls', createObject('bypass', 'AzureServices', 'defaultAction', if(parameters('coreConfig').network.isPrivate, 'Deny', 'Allow')))))]" + }, + "blobPrivateDnsZone": { + "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2024-06-01", + "name": "[format('privatelink.blob.{0}', environment().suffixes.storage)]" + }, + "blobEndpoint": { + "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[format('{0}-blob-ep', variables('appConfig').publisher.storage)]", + "location": "[parameters('coreConfig').hub.location]", + "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateEndpoints')]", + "properties": { + "subnet": { + "id": "[parameters('coreConfig').network.subnets.storage]" + }, + "privateLinkServiceConnections": [ + { + "name": "blobLink", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('appConfig').publisher.storage)]", + "groupIds": [ + "blob" + ] + } + } + ] + }, + "dependsOn": [ + "storageAccount" + ] + }, + "dfsPrivateDnsZone": { + "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2024-06-01", + "name": "[format('privatelink.dfs.{0}', environment().suffixes.storage)]" + }, + "dfsEndpoint": { + "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[format('{0}-dfs-ep', variables('appConfig').publisher.storage)]", + "location": "[parameters('coreConfig').hub.location]", + "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateEndpoints')]", + "properties": { + "subnet": { + "id": "[parameters('coreConfig').network.subnets.storage]" + }, + "privateLinkServiceConnections": [ + { + "name": "dfsLink", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('appConfig').publisher.storage)]", + "groupIds": [ + "dfs" + ] + } + } + ] + }, + "dependsOn": [ + "storageAccount" + ] + }, + "keyVault": { + "condition": "[variables('usesKeyVault')]", + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "name": "[variables('appConfig').publisher.keyVault]", + "location": "[parameters('coreConfig').hub.location]", + "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.KeyVault/vaults')]", + "properties": { + "sku": { + "name": "[parameters('coreConfig').keyVault.sku]", + "family": "A" + }, + "enabledForDeployment": true, + "enabledForTemplateDeployment": true, + "enabledForDiskEncryption": true, + "enableSoftDelete": true, + "softDeleteRetentionInDays": 90, + "enableRbacAuthorization": false, + "createMode": "default", + "tenantId": "[subscription().tenantId]", + "accessPolicies": [ + { + "objectId": "[reference('dataFactory', '2018-06-01', 'full').identity.principalId]", + "tenantId": "[subscription().tenantId]", + "permissions": { + "secrets": [ + "get" + ] + } + } + ], + "networkAcls": { + "bypass": "AzureServices", + "defaultAction": "[if(parameters('coreConfig').network.isPrivate, 'Deny', 'Allow')]" + } + }, + "dependsOn": [ + "dataFactory" + ] + }, + "keyVaultPrivateDnsZone": { + "condition": "[and(variables('usesKeyVault'), parameters('coreConfig').network.isPrivate)]", + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2024-06-01", + "name": "[format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore'))]", + "location": "global", + "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateDnsZones')]", + "properties": {} + }, + "keyVaultEndpoint": { + "condition": "[and(variables('usesKeyVault'), parameters('coreConfig').network.isPrivate)]", + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[format('{0}-ep', variables('appConfig').publisher.keyVault)]", + "location": "[parameters('coreConfig').hub.location]", + "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateEndpoints')]", + "properties": { + "subnet": { + "id": "[parameters('coreConfig').network.subnets.keyVault]" + }, + "privateLinkServiceConnections": [ + { + "name": "keyVaultLink", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.KeyVault/vaults', variables('appConfig').publisher.keyVault)]", + "groupIds": [ + "vault" + ] + } + } + ] + }, + "dependsOn": [ + "keyVault" + ] + } + }, + "outputs": { + "config": { + "$ref": "#/definitions/HubAppConfig", + "metadata": { + "description": "FinOps hub app configuration." + }, + "value": "[variables('appConfig')]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Principal ID for the managed identity used by Data Factory." + }, + "value": "[reference('dataFactory', '2018-06-01', 'full').identity.principalId]" + } + } +} \ No newline at end of file From 66f9c8fb34d8b13a2397856ff7f05c1d73282887 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 May 2025 19:03:50 +0000 Subject: [PATCH 5/6] Reverted custom resource naming parameters Co-authored-by: flanakin <399533+flanakin@users.noreply.github.com> --- src/templates/finops-hub/main.bicep | 12 - .../finops-hub/modules/hub-app.bicep | 14 +- src/templates/finops-hub/modules/hub-app.json | 905 ------------------ .../finops-hub/modules/hub-types.bicep | 21 +- .../finops-hub/modules/hub-types.json | 671 ------------- src/templates/finops-hub/modules/hub.bicep | 12 - 6 files changed, 7 insertions(+), 1628 deletions(-) delete mode 100644 src/templates/finops-hub/modules/hub-app.json delete mode 100644 src/templates/finops-hub/modules/hub-types.json diff --git a/src/templates/finops-hub/main.bicep b/src/templates/finops-hub/main.bicep index c0d709be5..f0a6dde6d 100644 --- a/src/templates/finops-hub/main.bicep +++ b/src/templates/finops-hub/main.bicep @@ -147,15 +147,6 @@ param enablePublicAccess bool = true @description('Optional. Address space for the workload. A /26 is required for the workload. Default: "10.20.30.0/26".') param virtualNetworkAddressPrefix string = '10.20.30.0/26' -@description('Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name. Must follow Azure Storage naming rules.') -param storageAccountName string = '' - -@description('Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name. Must follow Azure Data Factory naming rules.') -param dataFactoryName string = '' - -@description('Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name. Must follow Azure Key Vault naming rules.') -param keyVaultName string = '' - //============================================================================== // Resources @@ -185,9 +176,6 @@ module hub 'modules/hub.bicep' = { remoteHubStorageKey: remoteHubStorageKey enablePublicAccess: enablePublicAccess virtualNetworkAddressPrefix: virtualNetworkAddressPrefix - storageAccountName: storageAccountName - dataFactoryName: dataFactoryName - keyVaultName: keyVaultName } } diff --git a/src/templates/finops-hub/modules/hub-app.bicep b/src/templates/finops-hub/modules/hub-app.bicep index aff1b32a4..c034a59c2 100644 --- a/src/templates/finops-hub/modules/hub-app.bicep +++ b/src/templates/finops-hub/modules/hub-app.bicep @@ -38,15 +38,6 @@ param features HubAppFeature[] = [] @description('Optional. Custom string with additional metadata to log. Must an alphanumeric string without spaces or special characters except for underscores and dashes. Namespace + appName + telemetryString must be 50 characters or less - additional characters will be trimmed.') param telemetryString string = '' -@description('Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name.') -param storageAccountName string = '' - -@description('Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name.') -param dataFactoryName string = '' - -@description('Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name.') -param keyVaultName string = '' - //------------------------------------------------------------------------------ // Temporary parameters that should be removed in the future //------------------------------------------------------------------------------ @@ -66,10 +57,7 @@ var appConfig = newAppConfig( namespace, appName, displayName, - appVersion, - storageAccountName, - dataFactoryName, - keyVaultName + appVersion ) // Features diff --git a/src/templates/finops-hub/modules/hub-app.json b/src/templates/finops-hub/modules/hub-app.json deleted file mode 100644 index d47a10282..000000000 --- a/src/templates/finops-hub/modules/hub-app.json +++ /dev/null @@ -1,905 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.35.1.17967", - "templateHash": "14670356862329858753" - } - }, - "definitions": { - "_1.HubDeploymentConfig": { - "type": "object", - "properties": { - "tagsByResource": { - "type": "object" - }, - "storage": { - "type": "string" - }, - "isTelemetryEnabled": { - "type": "bool" - } - }, - "metadata": { - "tagsByResource": "Tags to apply to resources based on their resource type.", - "storage": "Name of the storage account used for deployment scripts.", - "isTelemetryEnabled": "Indicates whether telemetry should be enabled for deployments.", - "description": "FinOps hub deployment settings.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "_1.HubInstanceConfig": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "safeName": { - "type": "string" - }, - "suffix": { - "type": "string" - }, - "location": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "version": { - "type": "string" - } - }, - "metadata": { - "id": "FinOps hub resource ID.", - "name": "FinOps hub instance name.", - "safeName": "Safe name of the FinOps hub instance without underscores.", - "suffix": "Unique suffix used for shared resources.", - "location": "Azure resource location of the FinOps hub instance.", - "tags": "Tags to apply to all FinOps hub resources.", - "version": "FinOps hub version number.", - "description": "FinOps hub instance details.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "_1.HubNetworkConfig": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "isPrivate": { - "type": "bool" - }, - "addressPrefix": { - "type": "string" - }, - "dnsZones": { - "type": "object", - "properties": { - "blob": { - "$ref": "#/definitions/_1.IdNameObject" - }, - "dfs": { - "$ref": "#/definitions/_1.IdNameObject" - }, - "queue": { - "$ref": "#/definitions/_1.IdNameObject" - }, - "table": { - "$ref": "#/definitions/_1.IdNameObject" - } - } - }, - "subnets": { - "type": "object", - "properties": { - "dataFactory": { - "type": "string" - }, - "keyVault": { - "type": "string" - }, - "scripts": { - "type": "string" - }, - "storage": { - "type": "string" - } - } - } - }, - "metadata": { - "id": "Resource ID of the FinOps hub isolated virtual network, if private networking is enabled.", - "name": "Name of the FinOps hub isolated virtual network, if private networking is enabled.", - "isPrivate": "Indicates whether private networking is enabled.", - "addressPrefix": "Address prefix for the FinOps hub isolated virtual network, if private networking is enabled.", - "dnsZones": { - "blob": "Resource ID and name for the blob storage DNS zone.", - "dfs": "Resource ID and name for the DFS storage DNS zone.", - "queue": "Resource ID and name for the queue storage DNS zone.", - "table": "Resource ID and name for the table storage DNS zone." - }, - "subnets": { - "dataFactory": "Resource ID of the subnet for Data Factory instances.", - "keyVault": "Resource ID of the subnet for Key Vault instances.", - "scripts": "Resource ID of the subnet for deployment script storage.", - "storage": "Resource ID of the subnet for storage accounts." - }, - "description": "FinOps hub network settings.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "_1.HubStorageConfig": { - "type": "object", - "properties": { - "sku": { - "type": "string" - }, - "isInfrastructureEncrypted": { - "type": "bool" - } - }, - "metadata": { - "sku": "Storage account SKU. Allowed values: \"Premium_LRS\", \"Premium_ZRS\".", - "isInfrastructureEncrypted": "Indicates whether infrastructure encryption is enabled for the storage account.", - "description": "FinOps hub storage settings to be used when creating storage accounts.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "_1.HubVaultConfig": { - "type": "object", - "properties": { - "sku": { - "type": "string" - } - }, - "metadata": { - "sku": "KeyVault SKU. Allowed values: \"standard\", \"premium\".", - "description": "FinOps hub KeyVault settings to be used when creating vaults.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "_1.IdNameObject": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "metadata": { - "id": "Fully-qualified resource ID.", - "name": "Resource name.", - "description": "Resource ID and name.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "HubAppConfig": { - "type": "object", - "properties": { - "hub": { - "$ref": "#/definitions/_1.HubInstanceConfig" - }, - "deployment": { - "$ref": "#/definitions/_1.HubDeploymentConfig" - }, - "storage": { - "$ref": "#/definitions/_1.HubStorageConfig" - }, - "keyVault": { - "$ref": "#/definitions/_1.HubVaultConfig" - }, - "network": { - "$ref": "#/definitions/_1.HubNetworkConfig" - }, - "publisher": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "suffix": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "dataFactory": { - "type": "string" - }, - "keyVault": { - "type": "string" - }, - "storage": { - "type": "string" - } - } - }, - "app": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "fullName": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "tags": { - "type": "object" - } - } - } - }, - "metadata": { - "hub": "FinOps hub instance details", - "deployment": "FinOps hub deployment details", - "storage": "FinOps hub storage details", - "network": "FinOps hub network details", - "publisher": { - "uniqueId": "Unique suffix used for publisher resources.", - "name": "Fully-qualified namespace of the FinOps hub app publisher.", - "displayName": "Display name of the FinOps hub app publisher.", - "tags": "Tags to apply to all FinOps hub resources for this FinOps hub app publisher.", - "dataFactory": "Name of the Data Factory instance for this publisher.", - "keyVault": "Name of the KeyVault instance for this publisher.", - "storage": "Name of the storage account for this publisher.", - "subnetId": "Resource ID of the private endpoint subnet for the storage account." - }, - "app": { - "name": "Short name of the FinOps hub app (not including the publisher namespace).", - "fullName": "Fully-qualified namespace of the FinOps hub app.", - "displayName": "Display name of the FinOps hub app.", - "tags": "Tags to apply to all FinOps hub resources for this FinOps hub app." - }, - "description": "FinOps hub app configuration settings.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "HubAppFeature": { - "type": "string", - "allowedValues": [ - "DataFactory", - "KeyVault", - "Storage" - ], - "metadata": { - "description": "FinOps hub app features.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "HubCoreConfig": { - "type": "object", - "properties": { - "hub": { - "$ref": "#/definitions/_1.HubInstanceConfig" - }, - "deployment": { - "$ref": "#/definitions/_1.HubDeploymentConfig" - }, - "storage": { - "$ref": "#/definitions/_1.HubStorageConfig" - }, - "keyVault": { - "$ref": "#/definitions/_1.HubVaultConfig" - }, - "network": { - "$ref": "#/definitions/_1.HubNetworkConfig" - } - }, - "metadata": { - "hub": "FinOps hub instance details", - "deployment": "FinOps hub deployment details", - "storage": "FinOps hub storage details", - "keyVault": "FinOps hub KeyVault details", - "network": "FinOps hub network details", - "description": "FinOps hub configuration settings.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - } - }, - "functions": [ - { - "namespace": "__bicep", - "members": { - "getAppTags": { - "parameters": [ - { - "$ref": "#/definitions/HubAppConfig", - "name": "config" - }, - { - "type": "string", - "name": "resourceType" - }, - { - "type": "bool", - "nullable": true, - "name": "forceAppTags" - } - ], - "output": { - "type": "object", - "value": "[union(if(or(variables('_1.usePublisherSpecificResources'), coalesce(parameters('forceAppTags'), false())), parameters('config').app.tags, parameters('config').hub.tags), coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" - }, - "metadata": { - "description": "Returns a tags dictionary that includes tags for the FinOps hub app.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "getPublisherTags": { - "parameters": [ - { - "$ref": "#/definitions/HubAppConfig", - "name": "config" - }, - { - "type": "string", - "name": "resourceType" - } - ], - "output": { - "type": "object", - "value": "[union(if(variables('_1.usePublisherSpecificResources'), parameters('config').publisher.tags, parameters('config').hub.tags), coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" - }, - "metadata": { - "description": "Returns a tags dictionary that includes tags for the FinOps hub app publisher.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - }, - "newAppConfig": { - "parameters": [ - { - "$ref": "#/definitions/HubCoreConfig", - "name": "config" - }, - { - "type": "string", - "name": "publisher" - }, - { - "type": "string", - "name": "namespace" - }, - { - "type": "string", - "name": "appName" - }, - { - "type": "string", - "name": "displayName" - }, - { - "type": "string", - "name": "version" - }, - { - "type": "string", - "name": "customStorageName" - }, - { - "type": "string", - "name": "customDataFactoryName" - }, - { - "type": "string", - "name": "customKeyVaultName" - } - ], - "output": { - "$ref": "#/definitions/HubAppConfig", - "value": "[_1.newAppInternalConfig(parameters('config'), parameters('publisher'), parameters('namespace'), if(or(not(variables('_1.usePublisherSpecificResources')), equals(parameters('namespace'), 'Microsoft.FinOpsToolkit.Hubs')), parameters('config').hub.suffix, uniqueString(parameters('namespace'))), createObject('ftk-hubapp-publisher', parameters('namespace')), parameters('appName'), format('{0}.{1}', parameters('namespace'), parameters('appName')), parameters('displayName'), parameters('version'), parameters('customStorageName'), parameters('customDataFactoryName'), parameters('customKeyVaultName'))]" - }, - "metadata": { - "description": "Creates a new FinOps hub app configuration object.", - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - } - } - }, - { - "namespace": "_1", - "members": { - "newAppInternalConfig": { - "parameters": [ - { - "$ref": "#/definitions/HubCoreConfig", - "name": "coreConfig" - }, - { - "type": "string", - "name": "publisher" - }, - { - "type": "string", - "name": "namespace" - }, - { - "type": "string", - "name": "publisherSuffix" - }, - { - "type": "object", - "name": "publisherTags" - }, - { - "type": "string", - "name": "appName" - }, - { - "type": "string", - "name": "appNamespace" - }, - { - "type": "string", - "name": "displayName" - }, - { - "type": "string", - "name": "version" - }, - { - "type": "string", - "name": "customStorageName" - }, - { - "type": "string", - "name": "customDataFactoryName" - }, - { - "type": "string", - "name": "customKeyVaultName" - } - ], - "output": { - "$ref": "#/definitions/HubAppConfig", - "value": "[shallowMerge(createArray(parameters('coreConfig'), createObject('publisher', createObject('name', parameters('namespace'), 'displayName', parameters('publisher'), 'suffix', parameters('publisherSuffix'), 'tags', union(parameters('coreConfig').hub.tags, parameters('publisherTags')), 'dataFactory', if(not(empty(parameters('customDataFactoryName'))), parameters('customDataFactoryName'), replace(format('{0}{1}', take(format('{0}-engine', replace(parameters('coreConfig').hub.name, '_', '-')), sub(63, length(parameters('publisherSuffix')))), parameters('publisherSuffix')), '--', '-')), 'keyVault', if(not(empty(parameters('customKeyVaultName'))), parameters('customKeyVaultName'), replace(format('{0}{1}', take(format('{0}-vault', replace(parameters('coreConfig').hub.name, '_', '-')), sub(24, length(parameters('publisherSuffix')))), parameters('publisherSuffix')), '--', '-')), 'storage', if(not(empty(parameters('customStorageName'))), parameters('customStorageName'), format('{0}{1}', take(parameters('coreConfig').hub.safeName, sub(24, length(parameters('publisherSuffix')))), parameters('publisherSuffix')))), 'app', createObject('name', parameters('appName'), 'fullName', parameters('appNamespace'), 'displayName', parameters('displayName'), 'tags', union(parameters('coreConfig').hub.tags, parameters('publisherTags'), createObject('ftk-hubapp', parameters('appNamespace'), 'ftk-hubapp-version', parameters('version')))))))]" - }, - "metadata": { - "__bicep_imported_from!": { - "sourceTemplate": "hub-types.bicep" - } - } - } - } - } - ], - "parameters": { - "publisher": { - "type": "string", - "metadata": { - "description": "Required. Display name of the FinOps hub app publisher." - } - }, - "namespace": { - "type": "string", - "metadata": { - "description": "Required. Namespace to use for the FinOps hub app publisher. Will be combined with appName to form a fully-qualified identifier. Must be an alphanumeric string without spaces or special characters except for periods. This value should never change and will be used to uniquely identify the publisher. A change would require migrating content to the new publisher. Namespace + appName + telemetryString must be 50 characters or less - additional characters will be trimmed." - } - }, - "appName": { - "type": "string", - "metadata": { - "description": "Required. Unique identifier of the FinOps hub app within the publisher namespace. Must be an alphanumeric string without spaces or special characters. This name should never change and will be used with the namespace to fully qualify the app. A change would require migrating content to the new app. Namespace + appName + telemetryString must be 50 characters or less - additional characters will be trimmed." - } - }, - "displayName": { - "type": "string", - "metadata": { - "description": "Required. Display name of the FinOps hub app." - } - }, - "appVersion": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Version number of the FinOps hub app." - } - }, - "features": { - "type": "array", - "items": { - "$ref": "#/definitions/HubAppFeature" - }, - "defaultValue": [], - "metadata": { - "description": "Optional. Indicate which features the app requires. Allowed values: \"Storage\". Default: [] (none)." - } - }, - "telemetryString": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Custom string with additional metadata to log. Must an alphanumeric string without spaces or special characters except for underscores and dashes. Namespace + appName + telemetryString must be 50 characters or less - additional characters will be trimmed." - } - }, - "storageAccountName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name." - } - }, - "dataFactoryName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name." - } - }, - "keyVaultName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name." - } - }, - "coreConfig": { - "$ref": "#/definitions/HubCoreConfig", - "metadata": { - "description": "Required. FinOps hub coreConfig." - } - } - }, - "variables": { - "appConfig": "[__bicep.newAppConfig(parameters('coreConfig'), parameters('publisher'), parameters('namespace'), parameters('appName'), parameters('displayName'), parameters('appVersion'), parameters('storageAccountName'), parameters('dataFactoryName'), parameters('keyVaultName'))]", - "usesDataFactory": "[contains(parameters('features'), 'DataFactory')]", - "usesKeyVault": "[contains(parameters('features'), 'KeyVault')]", - "usesStorage": "[contains(parameters('features'), 'Storage')]", - "telemetryId": "[format('ftk-hubapp-{0}{1}{2}', variables('appConfig').app.fullName, if(empty(parameters('telemetryString')), '', '_'), parameters('telemetryString'))]", - "telemetryProps": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "[format('FTK: {0} - {1} {2}', parameters('publisher'), parameters('displayName'), variables('telemetryId'))]", - "version": "[parameters('appVersion')]" - } - }, - "resources": [] - } - }, - "storageInfrastructureEncryptionProperties": "[if(not(parameters('coreConfig').storage.isInfrastructureEncrypted), createObject(), createObject('encryption', createObject('keySource', 'Microsoft.Storage', 'requireInfrastructureEncryption', parameters('coreConfig').storage.isInfrastructureEncrypted)))]", - "_1.usePublisherSpecificResources": false - }, - "resources": { - "blobEndpoint::blobPrivateDnsZoneGroup": { - "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', format('{0}-blob-ep', variables('appConfig').publisher.storage), 'storage-endpoint-zone')]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('privatelink.blob.{0}', environment().suffixes.storage)]", - "properties": { - "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', format('privatelink.blob.{0}', environment().suffixes.storage))]" - } - } - ] - }, - "dependsOn": [ - "blobEndpoint" - ] - }, - "dfsEndpoint::dfsPrivateDnsZoneGroup": { - "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', format('{0}-dfs-ep', variables('appConfig').publisher.storage), 'dfs-endpoint-zone')]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('privatelink.dfs.{0}', environment().suffixes.storage)]", - "properties": { - "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', format('privatelink.dfs.{0}', environment().suffixes.storage))]" - } - } - ] - }, - "dependsOn": [ - "dfsEndpoint" - ] - }, - "keyVault::keyVault_accessPolicies": { - "condition": "[variables('usesKeyVault')]", - "type": "Microsoft.KeyVault/vaults/accessPolicies", - "apiVersion": "2023-02-01", - "name": "[format('{0}/{1}', variables('appConfig').publisher.keyVault, 'add')]", - "properties": { - "accessPolicies": [ - { - "objectId": "[reference('dataFactory', '2018-06-01', 'full').identity.principalId]", - "tenantId": "[subscription().tenantId]", - "permissions": { - "secrets": [ - "get" - ] - } - } - ] - }, - "dependsOn": [ - "dataFactory", - "keyVault" - ] - }, - "keyVaultPrivateDnsZone::keyVaultPrivateDnsZoneLink": { - "condition": "[and(variables('usesKeyVault'), parameters('coreConfig').network.isPrivate)]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore')), format('{0}-link', replace(format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore')), '.', '-')))]", - "location": "global", - "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateDnsZones/virtualNetworkLinks')]", - "properties": { - "virtualNetwork": { - "id": "[parameters('coreConfig').network.id]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "keyVaultPrivateDnsZone" - ] - }, - "keyVaultEndpoint::keyVaultPrivateDnsZoneGroup": { - "condition": "[and(variables('usesKeyVault'), parameters('coreConfig').network.isPrivate)]", - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-11-01", - "name": "[format('{0}/{1}', format('{0}-ep', variables('appConfig').publisher.keyVault), 'keyvault-endpoint-zone')]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore'))]", - "properties": { - "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore')))]" - } - } - ] - }, - "dependsOn": [ - "keyVaultEndpoint", - "keyVaultPrivateDnsZone" - ] - }, - "appTelemetry": { - "condition": "[parameters('coreConfig').deployment.isTelemetryEnabled]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[if(lessOrEquals(length(variables('telemetryId')), 64), variables('telemetryId'), substring(variables('telemetryId'), 0, 64))]", - "tags": "[__bicep.getAppTags(variables('appConfig'), 'Microsoft.Resources/deployments', true())]", - "properties": "[variables('telemetryProps')]" - }, - "dataFactory": { - "condition": "[variables('usesDataFactory')]", - "type": "Microsoft.DataFactory/factories", - "apiVersion": "2018-06-01", - "name": "[variables('appConfig').publisher.dataFactory]", - "location": "[variables('appConfig').hub.location]", - "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.DataFactory/factories')]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "globalConfigurations": { - "PipelineBillingEnabled": "true" - } - } - }, - "storageAccount": { - "condition": "[variables('usesStorage')]", - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2022-09-01", - "name": "[variables('appConfig').publisher.storage]", - "location": "[parameters('coreConfig').hub.location]", - "sku": { - "name": "[parameters('coreConfig').storage.sku]" - }, - "kind": "BlockBlobStorage", - "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Storage/storageAccounts')]", - "properties": "[shallowMerge(createArray(variables('storageInfrastructureEncryptionProperties'), createObject('supportsHttpsTrafficOnly', true(), 'allowSharedKeyAccess', true(), 'isHnsEnabled', true(), 'minimumTlsVersion', 'TLS1_2', 'allowBlobPublicAccess', false(), 'publicNetworkAccess', 'Enabled', 'networkAcls', createObject('bypass', 'AzureServices', 'defaultAction', if(parameters('coreConfig').network.isPrivate, 'Deny', 'Allow')))))]" - }, - "blobPrivateDnsZone": { - "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2024-06-01", - "name": "[format('privatelink.blob.{0}', environment().suffixes.storage)]" - }, - "blobEndpoint": { - "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[format('{0}-blob-ep', variables('appConfig').publisher.storage)]", - "location": "[parameters('coreConfig').hub.location]", - "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateEndpoints')]", - "properties": { - "subnet": { - "id": "[parameters('coreConfig').network.subnets.storage]" - }, - "privateLinkServiceConnections": [ - { - "name": "blobLink", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('appConfig').publisher.storage)]", - "groupIds": [ - "blob" - ] - } - } - ] - }, - "dependsOn": [ - "storageAccount" - ] - }, - "dfsPrivateDnsZone": { - "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", - "existing": true, - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2024-06-01", - "name": "[format('privatelink.dfs.{0}', environment().suffixes.storage)]" - }, - "dfsEndpoint": { - "condition": "[and(variables('usesStorage'), parameters('coreConfig').network.isPrivate)]", - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[format('{0}-dfs-ep', variables('appConfig').publisher.storage)]", - "location": "[parameters('coreConfig').hub.location]", - "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateEndpoints')]", - "properties": { - "subnet": { - "id": "[parameters('coreConfig').network.subnets.storage]" - }, - "privateLinkServiceConnections": [ - { - "name": "dfsLink", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('appConfig').publisher.storage)]", - "groupIds": [ - "dfs" - ] - } - } - ] - }, - "dependsOn": [ - "storageAccount" - ] - }, - "keyVault": { - "condition": "[variables('usesKeyVault')]", - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2023-02-01", - "name": "[variables('appConfig').publisher.keyVault]", - "location": "[parameters('coreConfig').hub.location]", - "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.KeyVault/vaults')]", - "properties": { - "sku": { - "name": "[parameters('coreConfig').keyVault.sku]", - "family": "A" - }, - "enabledForDeployment": true, - "enabledForTemplateDeployment": true, - "enabledForDiskEncryption": true, - "enableSoftDelete": true, - "softDeleteRetentionInDays": 90, - "enableRbacAuthorization": false, - "createMode": "default", - "tenantId": "[subscription().tenantId]", - "accessPolicies": [ - { - "objectId": "[reference('dataFactory', '2018-06-01', 'full').identity.principalId]", - "tenantId": "[subscription().tenantId]", - "permissions": { - "secrets": [ - "get" - ] - } - } - ], - "networkAcls": { - "bypass": "AzureServices", - "defaultAction": "[if(parameters('coreConfig').network.isPrivate, 'Deny', 'Allow')]" - } - }, - "dependsOn": [ - "dataFactory" - ] - }, - "keyVaultPrivateDnsZone": { - "condition": "[and(variables('usesKeyVault'), parameters('coreConfig').network.isPrivate)]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2024-06-01", - "name": "[format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore'))]", - "location": "global", - "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateDnsZones')]", - "properties": {} - }, - "keyVaultEndpoint": { - "condition": "[and(variables('usesKeyVault'), parameters('coreConfig').network.isPrivate)]", - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-11-01", - "name": "[format('{0}-ep', variables('appConfig').publisher.keyVault)]", - "location": "[parameters('coreConfig').hub.location]", - "tags": "[__bicep.getPublisherTags(variables('appConfig'), 'Microsoft.Network/privateEndpoints')]", - "properties": { - "subnet": { - "id": "[parameters('coreConfig').network.subnets.keyVault]" - }, - "privateLinkServiceConnections": [ - { - "name": "keyVaultLink", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.KeyVault/vaults', variables('appConfig').publisher.keyVault)]", - "groupIds": [ - "vault" - ] - } - } - ] - }, - "dependsOn": [ - "keyVault" - ] - } - }, - "outputs": { - "config": { - "$ref": "#/definitions/HubAppConfig", - "metadata": { - "description": "FinOps hub app configuration." - }, - "value": "[variables('appConfig')]" - }, - "principalId": { - "type": "string", - "metadata": { - "description": "Principal ID for the managed identity used by Data Factory." - }, - "value": "[reference('dataFactory', '2018-06-01', 'full').identity.principalId]" - } - } -} \ No newline at end of file diff --git a/src/templates/finops-hub/modules/hub-types.bicep b/src/templates/finops-hub/modules/hub-types.bicep index 73ba0e3ad..bcc155672 100644 --- a/src/templates/finops-hub/modules/hub-types.bicep +++ b/src/templates/finops-hub/modules/hub-types.bicep @@ -348,10 +348,7 @@ func newAppInternalConfig( appName string, appNamespace string, displayName string, - version string, - customStorageName string, - customDataFactoryName string, - customKeyVaultName string + version string ) HubAppConfig => { ...coreConfig publisher: { @@ -362,13 +359,13 @@ func newAppInternalConfig( tags: union(coreConfig.hub.tags, publisherTags) // Globally unique Data Factory name: 3-63 chars; letters, numbers, non-repeating dashes - dataFactory: !empty(customDataFactoryName) ? customDataFactoryName : replace('${take('${replace(coreConfig.hub.name, '_', '-')}-engine', 63 - length(publisherSuffix))}${publisherSuffix}', '--', '-') + dataFactory: replace('${take('${replace(coreConfig.hub.name, '_', '-')}-engine', 63 - length(publisherSuffix))}${publisherSuffix}', '--', '-') // Globally unique KeyVault name: 3-24 chars; letters, numbers, dashes - keyVault: !empty(customKeyVaultName) ? customKeyVaultName : replace('${take('${replace(coreConfig.hub.name, '_', '-')}-vault', 24 - length(publisherSuffix))}${publisherSuffix}', '--', '-') + keyVault: replace('${take('${replace(coreConfig.hub.name, '_', '-')}-vault', 24 - length(publisherSuffix))}${publisherSuffix}', '--', '-') // Globally unique storage account name: 3-24 chars; lowercase letters/numbers only - storage: !empty(customStorageName) ? customStorageName : '${take(coreConfig.hub.safeName, 24 - length(publisherSuffix))}${publisherSuffix}' + storage: '${take(coreConfig.hub.safeName, 24 - length(publisherSuffix))}${publisherSuffix}' } app: { // id: '${hubResourceId}/publishers/${namespace}/apps/${appName}' @@ -391,10 +388,7 @@ func newAppConfig( namespace string, appName string, displayName string, - version string, - customStorageName string, - customDataFactoryName string, - customKeyVaultName string + version string ) HubAppConfig => newAppInternalConfig( config, publisher, @@ -404,10 +398,7 @@ func newAppConfig( appName, '${namespace}.${appName}', // appNamespace displayName, - version, - customStorageName, - customDataFactoryName, - customKeyVaultName + version ) @export() diff --git a/src/templates/finops-hub/modules/hub-types.json b/src/templates/finops-hub/modules/hub-types.json deleted file mode 100644 index 177f85c95..000000000 --- a/src/templates/finops-hub/modules/hub-types.json +++ /dev/null @@ -1,671 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "languageVersion": "2.0", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.35.1.17967", - "templateHash": "3328278183267243822" - } - }, - "definitions": { - "IdNameObject": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "metadata": { - "id": "Fully-qualified resource ID.", - "name": "Resource name.", - "description": "Resource ID and name." - } - }, - "HubInstanceConfig": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "safeName": { - "type": "string" - }, - "suffix": { - "type": "string" - }, - "location": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "version": { - "type": "string" - } - }, - "metadata": { - "id": "FinOps hub resource ID.", - "name": "FinOps hub instance name.", - "safeName": "Safe name of the FinOps hub instance without underscores.", - "suffix": "Unique suffix used for shared resources.", - "location": "Azure resource location of the FinOps hub instance.", - "tags": "Tags to apply to all FinOps hub resources.", - "version": "FinOps hub version number.", - "description": "FinOps hub instance details." - } - }, - "HubDeploymentConfig": { - "type": "object", - "properties": { - "tagsByResource": { - "type": "object" - }, - "storage": { - "type": "string" - }, - "isTelemetryEnabled": { - "type": "bool" - } - }, - "metadata": { - "tagsByResource": "Tags to apply to resources based on their resource type.", - "storage": "Name of the storage account used for deployment scripts.", - "isTelemetryEnabled": "Indicates whether telemetry should be enabled for deployments.", - "description": "FinOps hub deployment settings." - } - }, - "HubStorageConfig": { - "type": "object", - "properties": { - "sku": { - "type": "string" - }, - "isInfrastructureEncrypted": { - "type": "bool" - } - }, - "metadata": { - "sku": "Storage account SKU. Allowed values: \"Premium_LRS\", \"Premium_ZRS\".", - "isInfrastructureEncrypted": "Indicates whether infrastructure encryption is enabled for the storage account.", - "description": "FinOps hub storage settings to be used when creating storage accounts." - } - }, - "HubVaultConfig": { - "type": "object", - "properties": { - "sku": { - "type": "string" - } - }, - "metadata": { - "sku": "KeyVault SKU. Allowed values: \"standard\", \"premium\".", - "description": "FinOps hub KeyVault settings to be used when creating vaults." - } - }, - "HubNetworkConfig": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "isPrivate": { - "type": "bool" - }, - "addressPrefix": { - "type": "string" - }, - "dnsZones": { - "type": "object", - "properties": { - "blob": { - "$ref": "#/definitions/IdNameObject" - }, - "dfs": { - "$ref": "#/definitions/IdNameObject" - }, - "queue": { - "$ref": "#/definitions/IdNameObject" - }, - "table": { - "$ref": "#/definitions/IdNameObject" - } - } - }, - "subnets": { - "type": "object", - "properties": { - "dataFactory": { - "type": "string" - }, - "keyVault": { - "type": "string" - }, - "scripts": { - "type": "string" - }, - "storage": { - "type": "string" - } - } - } - }, - "metadata": { - "id": "Resource ID of the FinOps hub isolated virtual network, if private networking is enabled.", - "name": "Name of the FinOps hub isolated virtual network, if private networking is enabled.", - "isPrivate": "Indicates whether private networking is enabled.", - "addressPrefix": "Address prefix for the FinOps hub isolated virtual network, if private networking is enabled.", - "dnsZones": { - "blob": "Resource ID and name for the blob storage DNS zone.", - "dfs": "Resource ID and name for the DFS storage DNS zone.", - "queue": "Resource ID and name for the queue storage DNS zone.", - "table": "Resource ID and name for the table storage DNS zone." - }, - "subnets": { - "dataFactory": "Resource ID of the subnet for Data Factory instances.", - "keyVault": "Resource ID of the subnet for Key Vault instances.", - "scripts": "Resource ID of the subnet for deployment script storage.", - "storage": "Resource ID of the subnet for storage accounts." - }, - "description": "FinOps hub network settings." - } - }, - "HubCoreConfig": { - "type": "object", - "properties": { - "hub": { - "$ref": "#/definitions/HubInstanceConfig" - }, - "deployment": { - "$ref": "#/definitions/HubDeploymentConfig" - }, - "storage": { - "$ref": "#/definitions/HubStorageConfig" - }, - "keyVault": { - "$ref": "#/definitions/HubVaultConfig" - }, - "network": { - "$ref": "#/definitions/HubNetworkConfig" - } - }, - "metadata": { - "__bicep_export!": true, - "hub": "FinOps hub instance details", - "deployment": "FinOps hub deployment details", - "storage": "FinOps hub storage details", - "keyVault": "FinOps hub KeyVault details", - "network": "FinOps hub network details", - "description": "FinOps hub configuration settings." - } - }, - "HubAppConfig": { - "type": "object", - "properties": { - "hub": { - "$ref": "#/definitions/HubInstanceConfig" - }, - "deployment": { - "$ref": "#/definitions/HubDeploymentConfig" - }, - "storage": { - "$ref": "#/definitions/HubStorageConfig" - }, - "keyVault": { - "$ref": "#/definitions/HubVaultConfig" - }, - "network": { - "$ref": "#/definitions/HubNetworkConfig" - }, - "publisher": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "suffix": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "dataFactory": { - "type": "string" - }, - "keyVault": { - "type": "string" - }, - "storage": { - "type": "string" - } - } - }, - "app": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "fullName": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "tags": { - "type": "object" - } - } - } - }, - "metadata": { - "__bicep_export!": true, - "hub": "FinOps hub instance details", - "deployment": "FinOps hub deployment details", - "storage": "FinOps hub storage details", - "network": "FinOps hub network details", - "publisher": { - "uniqueId": "Unique suffix used for publisher resources.", - "name": "Fully-qualified namespace of the FinOps hub app publisher.", - "displayName": "Display name of the FinOps hub app publisher.", - "tags": "Tags to apply to all FinOps hub resources for this FinOps hub app publisher.", - "dataFactory": "Name of the Data Factory instance for this publisher.", - "keyVault": "Name of the KeyVault instance for this publisher.", - "storage": "Name of the storage account for this publisher.", - "subnetId": "Resource ID of the private endpoint subnet for the storage account." - }, - "app": { - "name": "Short name of the FinOps hub app (not including the publisher namespace).", - "fullName": "Fully-qualified namespace of the FinOps hub app.", - "displayName": "Display name of the FinOps hub app.", - "tags": "Tags to apply to all FinOps hub resources for this FinOps hub app." - }, - "description": "FinOps hub app configuration settings." - } - }, - "HubAppFeature": { - "type": "string", - "allowedValues": [ - "DataFactory", - "KeyVault", - "Storage" - ], - "metadata": { - "__bicep_export!": true, - "description": "FinOps hub app features." - } - } - }, - "functions": [ - { - "namespace": "__bicep", - "members": { - "safeName": { - "parameters": [ - { - "type": "string", - "name": "name" - } - ], - "output": { - "type": "string", - "value": "[replace(replace(toLower(parameters('name')), '-', ''), '_', '')]" - } - }, - "idName": { - "parameters": [ - { - "type": "string", - "name": "name" - }, - { - "type": "string", - "name": "resourceType" - } - ], - "output": { - "$ref": "#/definitions/IdNameObject", - "value": { - "id": "[resourceId(parameters('resourceType'), parameters('name'))]", - "name": "[parameters('name')]" - } - } - }, - "dnsZoneIdName": { - "parameters": [ - { - "type": "string", - "name": "type" - } - ], - "output": { - "$ref": "#/definitions/IdNameObject", - "value": "[__bicep.idName(format('privatelink.{0}.{1}', parameters('type'), environment().suffixes.storage), 'Microsoft.Network/privateDnsZones')]" - } - }, - "newHubCoreConfigInternal": { - "parameters": [ - { - "type": "string", - "name": "id" - }, - { - "type": "string", - "name": "name" - }, - { - "type": "string", - "name": "safeName" - }, - { - "type": "string", - "name": "suffix" - }, - { - "type": "string", - "name": "location" - }, - { - "type": "object", - "name": "tags" - }, - { - "type": "object", - "name": "tagsByResource" - }, - { - "type": "string", - "name": "storageSku" - }, - { - "type": "string", - "name": "keyVaultSku" - }, - { - "type": "bool", - "name": "enableInfrastructureEncryption" - }, - { - "type": "bool", - "name": "enablePublicAccess" - }, - { - "type": "string", - "name": "networkName" - }, - { - "type": "string", - "name": "networkAddressPrefix" - }, - { - "type": "bool", - "name": "isTelemetryEnabled" - } - ], - "output": { - "$ref": "#/definitions/HubCoreConfig", - "value": { - "hub": { - "id": "[parameters('id')]", - "name": "[parameters('name')]", - "safeName": "[parameters('safeName')]", - "suffix": "[parameters('suffix')]", - "location": "[coalesce(parameters('location'), resourceGroup().location)]", - "tags": "[union(parameters('tags'), createObject('cm-resource-parent', parameters('id'), 'ftk-tool', 'FinOps hubs', 'ftk-version', variables('finOpsToolkitVersion')))]", - "version": "[variables('finOpsToolkitVersion')]" - }, - "deployment": "[union(createObject('tagsByResource', parameters('tagsByResource'), 'isTelemetryEnabled', coalesce(parameters('isTelemetryEnabled'), true())), if(parameters('enablePublicAccess'), createObject('storage', ''), createObject('storage', format('{0}script{1}', take(parameters('safeName'), sub(16, length(parameters('suffix')))), parameters('suffix')))))]", - "storage": { - "sku": "[parameters('storageSku')]", - "isInfrastructureEncrypted": "[parameters('enableInfrastructureEncryption')]" - }, - "keyVault": { - "sku": "[parameters('keyVaultSku')]" - }, - "network": "[if(parameters('enablePublicAccess'), createObject('isPrivate', false(), 'id', '', 'name', '', 'addressPrefix', '', 'dnsZones', createObject('blob', createObject('id', '', 'name', ''), 'dfs', createObject('id', '', 'name', ''), 'queue', createObject('id', '', 'name', ''), 'table', createObject('id', '', 'name', '')), 'subnets', createObject('dataFactory', '', 'keyVault', '', 'scripts', '', 'storage', '')), createObject('isPrivate', true(), 'id', resourceId('Microsoft.Network/virtualNetworks', parameters('networkName')), 'name', parameters('networkName'), 'addressPrefix', parameters('networkAddressPrefix'), 'dnsZones', createObject('blob', __bicep.dnsZoneIdName('blob'), 'dfs', __bicep.dnsZoneIdName('dfs'), 'queue', __bicep.dnsZoneIdName('queue'), 'table', __bicep.dnsZoneIdName('table')), 'subnets', createObject('dataFactory', resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), 'private-endpoint-subnet'), 'keyVault', resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), 'private-endpoint-subnet'), 'scripts', resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), 'script-subnet'), 'storage', resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('networkName'), 'private-endpoint-subnet'))))]" - } - } - }, - "newHubCoreConfig": { - "parameters": [ - { - "type": "string", - "name": "name" - }, - { - "type": "string", - "name": "location" - }, - { - "type": "object", - "name": "tags" - }, - { - "type": "object", - "name": "tagsByResource" - }, - { - "type": "string", - "name": "storageSku" - }, - { - "type": "string", - "name": "keyVaultSku" - }, - { - "type": "bool", - "name": "enableInfrastructureEncryption" - }, - { - "type": "bool", - "name": "enablePublicAccess" - }, - { - "type": "string", - "name": "networkAddressPrefix" - }, - { - "type": "bool", - "name": "isTelemetryEnabled" - } - ], - "output": { - "$ref": "#/definitions/HubCoreConfig", - "value": "[__bicep.newHubCoreConfigInternal(format('{0}/providers/Microsoft.Cloud/hubs/{1}', resourceGroup().id, parameters('name')), parameters('name'), __bicep.safeName(parameters('name')), uniqueString(parameters('name'), resourceGroup().id), parameters('location'), parameters('tags'), parameters('tagsByResource'), parameters('storageSku'), parameters('keyVaultSku'), parameters('enableInfrastructureEncryption'), parameters('enablePublicAccess'), format('{0}-vnet-{1}', __bicep.safeName(parameters('name')), parameters('location')), parameters('networkAddressPrefix'), coalesce(parameters('isTelemetryEnabled'), true()))]" - }, - "metadata": { - "description": "Creates a new FinOps hub configuration object.", - "__bicep_export!": true - } - }, - "newAppInternalConfig": { - "parameters": [ - { - "$ref": "#/definitions/HubCoreConfig", - "name": "coreConfig" - }, - { - "type": "string", - "name": "publisher" - }, - { - "type": "string", - "name": "namespace" - }, - { - "type": "string", - "name": "publisherSuffix" - }, - { - "type": "object", - "name": "publisherTags" - }, - { - "type": "string", - "name": "appName" - }, - { - "type": "string", - "name": "appNamespace" - }, - { - "type": "string", - "name": "displayName" - }, - { - "type": "string", - "name": "version" - }, - { - "type": "string", - "name": "customStorageName" - }, - { - "type": "string", - "name": "customDataFactoryName" - }, - { - "type": "string", - "name": "customKeyVaultName" - } - ], - "output": { - "$ref": "#/definitions/HubAppConfig", - "value": "[shallowMerge(createArray(parameters('coreConfig'), createObject('publisher', createObject('name', parameters('namespace'), 'displayName', parameters('publisher'), 'suffix', parameters('publisherSuffix'), 'tags', union(parameters('coreConfig').hub.tags, parameters('publisherTags')), 'dataFactory', if(not(empty(parameters('customDataFactoryName'))), parameters('customDataFactoryName'), replace(format('{0}{1}', take(format('{0}-engine', replace(parameters('coreConfig').hub.name, '_', '-')), sub(63, length(parameters('publisherSuffix')))), parameters('publisherSuffix')), '--', '-')), 'keyVault', if(not(empty(parameters('customKeyVaultName'))), parameters('customKeyVaultName'), replace(format('{0}{1}', take(format('{0}-vault', replace(parameters('coreConfig').hub.name, '_', '-')), sub(24, length(parameters('publisherSuffix')))), parameters('publisherSuffix')), '--', '-')), 'storage', if(not(empty(parameters('customStorageName'))), parameters('customStorageName'), format('{0}{1}', take(parameters('coreConfig').hub.safeName, sub(24, length(parameters('publisherSuffix')))), parameters('publisherSuffix')))), 'app', createObject('name', parameters('appName'), 'fullName', parameters('appNamespace'), 'displayName', parameters('displayName'), 'tags', union(parameters('coreConfig').hub.tags, parameters('publisherTags'), createObject('ftk-hubapp', parameters('appNamespace'), 'ftk-hubapp-version', parameters('version')))))))]" - } - }, - "newAppConfig": { - "parameters": [ - { - "$ref": "#/definitions/HubCoreConfig", - "name": "config" - }, - { - "type": "string", - "name": "publisher" - }, - { - "type": "string", - "name": "namespace" - }, - { - "type": "string", - "name": "appName" - }, - { - "type": "string", - "name": "displayName" - }, - { - "type": "string", - "name": "version" - }, - { - "type": "string", - "name": "customStorageName" - }, - { - "type": "string", - "name": "customDataFactoryName" - }, - { - "type": "string", - "name": "customKeyVaultName" - } - ], - "output": { - "$ref": "#/definitions/HubAppConfig", - "value": "[__bicep.newAppInternalConfig(parameters('config'), parameters('publisher'), parameters('namespace'), if(or(not(variables('usePublisherSpecificResources')), equals(parameters('namespace'), 'Microsoft.FinOpsToolkit.Hubs')), parameters('config').hub.suffix, uniqueString(parameters('namespace'))), createObject('ftk-hubapp-publisher', parameters('namespace')), parameters('appName'), format('{0}.{1}', parameters('namespace'), parameters('appName')), parameters('displayName'), parameters('version'), parameters('customStorageName'), parameters('customDataFactoryName'), parameters('customKeyVaultName'))]" - }, - "metadata": { - "description": "Creates a new FinOps hub app configuration object.", - "__bicep_export!": true - } - }, - "getHubTags": { - "parameters": [ - { - "$ref": "#/definitions/HubCoreConfig", - "name": "config" - }, - { - "type": "string", - "name": "resourceType" - } - ], - "output": { - "type": "object", - "value": "[union(parameters('config').hub.tags, coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" - }, - "metadata": { - "description": "Returns a tags dictionary that includes tags for the FinOps hub instance.", - "__bicep_export!": true - } - }, - "getPublisherTags": { - "parameters": [ - { - "$ref": "#/definitions/HubAppConfig", - "name": "config" - }, - { - "type": "string", - "name": "resourceType" - } - ], - "output": { - "type": "object", - "value": "[union(if(variables('usePublisherSpecificResources'), parameters('config').publisher.tags, parameters('config').hub.tags), coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" - }, - "metadata": { - "description": "Returns a tags dictionary that includes tags for the FinOps hub app publisher.", - "__bicep_export!": true - } - }, - "getAppTags": { - "parameters": [ - { - "$ref": "#/definitions/HubAppConfig", - "name": "config" - }, - { - "type": "string", - "name": "resourceType" - }, - { - "type": "bool", - "nullable": true, - "name": "forceAppTags" - } - ], - "output": { - "type": "object", - "value": "[union(if(or(variables('usePublisherSpecificResources'), coalesce(parameters('forceAppTags'), false())), parameters('config').app.tags, parameters('config').hub.tags), coalesce(tryGet(parameters('config').deployment.tagsByResource, parameters('resourceType')), createObject()))]" - }, - "metadata": { - "description": "Returns a tags dictionary that includes tags for the FinOps hub app.", - "__bicep_export!": true - } - } - } - } - ], - "variables": { - "usePublisherSpecificResources": false, - "finOpsToolkitVersion": "0.11-dev" - }, - "resources": {} -} \ No newline at end of file diff --git a/src/templates/finops-hub/modules/hub.bicep b/src/templates/finops-hub/modules/hub.bicep index 5769f8474..68f5e59fb 100644 --- a/src/templates/finops-hub/modules/hub.bicep +++ b/src/templates/finops-hub/modules/hub.bicep @@ -158,15 +158,6 @@ param enablePublicAccess bool = true @description('Optional. Address space for the workload. A /26 is required for the workload. Default: "10.20.30.0/26".') param virtualNetworkAddressPrefix string = '10.20.30.0/26' -@description('Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name.') -param storageAccountName string = '' - -@description('Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name.') -param dataFactoryName string = '' - -@description('Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name.') -param keyVaultName string = '' - @description('Optional. Enable telemetry to track anonymous module usage trends, monitor for bugs, and improve future releases.') param enableDefaultTelemetry bool = true @@ -311,9 +302,6 @@ module appRegistration 'hub-app.bicep' = { 'Storage' ] telemetryString: telemetryString - storageAccountName: storageAccountName - dataFactoryName: dataFactoryName - keyVaultName: keyVaultName coreConfig: coreConfig } From 3cb5af8e5f038315f3c53d0dc90d9b23e2712ad2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 May 2025 19:14:31 +0000 Subject: [PATCH 6/6] Restored custom resource naming parameters in hub.bicep and hub-app.bicep Co-authored-by: flanakin <399533+flanakin@users.noreply.github.com> --- .../finops-hub/modules/hub-app.bicep | 14 ++++++++++++- .../finops-hub/modules/hub-types.bicep | 21 +++++++++++++------ src/templates/finops-hub/modules/hub.bicep | 13 ++++++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/templates/finops-hub/modules/hub-app.bicep b/src/templates/finops-hub/modules/hub-app.bicep index c034a59c2..aff1b32a4 100644 --- a/src/templates/finops-hub/modules/hub-app.bicep +++ b/src/templates/finops-hub/modules/hub-app.bicep @@ -38,6 +38,15 @@ param features HubAppFeature[] = [] @description('Optional. Custom string with additional metadata to log. Must an alphanumeric string without spaces or special characters except for underscores and dashes. Namespace + appName + telemetryString must be 50 characters or less - additional characters will be trimmed.') param telemetryString string = '' +@description('Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name.') +param storageAccountName string = '' + +@description('Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name.') +param dataFactoryName string = '' + +@description('Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name.') +param keyVaultName string = '' + //------------------------------------------------------------------------------ // Temporary parameters that should be removed in the future //------------------------------------------------------------------------------ @@ -57,7 +66,10 @@ var appConfig = newAppConfig( namespace, appName, displayName, - appVersion + appVersion, + storageAccountName, + dataFactoryName, + keyVaultName ) // Features diff --git a/src/templates/finops-hub/modules/hub-types.bicep b/src/templates/finops-hub/modules/hub-types.bicep index bcc155672..1620e0cd2 100644 --- a/src/templates/finops-hub/modules/hub-types.bicep +++ b/src/templates/finops-hub/modules/hub-types.bicep @@ -348,7 +348,10 @@ func newAppInternalConfig( appName string, appNamespace string, displayName string, - version string + version string, + customStorageName string, + customDataFactoryName string, + customKeyVaultName string ) HubAppConfig => { ...coreConfig publisher: { @@ -359,13 +362,13 @@ func newAppInternalConfig( tags: union(coreConfig.hub.tags, publisherTags) // Globally unique Data Factory name: 3-63 chars; letters, numbers, non-repeating dashes - dataFactory: replace('${take('${replace(coreConfig.hub.name, '_', '-')}-engine', 63 - length(publisherSuffix))}${publisherSuffix}', '--', '-') + dataFactory: !empty(customDataFactoryName) ? customDataFactoryName : replace('${take('${replace(coreConfig.hub.name, '_', '-')}-engine', 63 - length(publisherSuffix))}${publisherSuffix}', '--', '-') // Globally unique KeyVault name: 3-24 chars; letters, numbers, dashes - keyVault: replace('${take('${replace(coreConfig.hub.name, '_', '-')}-vault', 24 - length(publisherSuffix))}${publisherSuffix}', '--', '-') + keyVault: !empty(customKeyVaultName) ? customKeyVaultName : replace('${take('${replace(coreConfig.hub.name, '_', '-')}-vault', 24 - length(publisherSuffix))}${publisherSuffix}', '--', '-') // Globally unique storage account name: 3-24 chars; lowercase letters/numbers only - storage: '${take(coreConfig.hub.safeName, 24 - length(publisherSuffix))}${publisherSuffix}' + storage: !empty(customStorageName) ? customStorageName : '${take(coreConfig.hub.safeName, 24 - length(publisherSuffix))}${publisherSuffix}' } app: { // id: '${hubResourceId}/publishers/${namespace}/apps/${appName}' @@ -388,7 +391,10 @@ func newAppConfig( namespace string, appName string, displayName string, - version string + version string, + customStorageName string = '', + customDataFactoryName string = '', + customKeyVaultName string = '' ) HubAppConfig => newAppInternalConfig( config, publisher, @@ -398,7 +404,10 @@ func newAppConfig( appName, '${namespace}.${appName}', // appNamespace displayName, - version + version, + customStorageName, + customDataFactoryName, + customKeyVaultName ) @export() diff --git a/src/templates/finops-hub/modules/hub.bicep b/src/templates/finops-hub/modules/hub.bicep index 68f5e59fb..f4e9e7935 100644 --- a/src/templates/finops-hub/modules/hub.bicep +++ b/src/templates/finops-hub/modules/hub.bicep @@ -17,6 +17,16 @@ param location string = resourceGroup().location // @description('Optional. Azure location to use for a temporary Event Grid namespace to register the Microsoft.EventGrid resource provider if the primary location is not supported. The namespace will be deleted and is not used for hub operation. Default: "" (same as location).') // param eventGridLocation string = '' +@description('Optional. Custom name for the Storage Account. If not provided, a name will be generated based on the hub name.') +param storageAccountName string = '' + +@description('Optional. Custom name for the Data Factory. If not provided, a name will be generated based on the hub name.') +param dataFactoryName string = '' + +@description('Optional. Custom name for the Key Vault. If not provided, a name will be generated based on the hub name.') + +param keyVaultName string = '' + @allowed([ 'Premium_LRS' 'Premium_ZRS' @@ -302,6 +312,9 @@ module appRegistration 'hub-app.bicep' = { 'Storage' ] telemetryString: telemetryString + storageAccountName: storageAccountName + dataFactoryName: dataFactoryName + keyVaultName: keyVaultName coreConfig: coreConfig }